From 68485ace640a03472b1074cdb6d8369cd82e691d Mon Sep 17 00:00:00 2001 From: "mjw@wray-m-3.hpl.hp.com" Date: Mon, 5 Jul 2004 16:23:43 +0000 Subject: [PATCH] bitkeeper revision 1.1041.5.7 (40e9808fAoDayBeZwAauQ7CRlcKCog) Start of code to talk to migration daemon to do migrate. Initial checkin of the migration daemon, xfrd. --- .rootkeys | 27 + tools/libxutil/string_stream.c | 47 +- tools/python/xen/xend/XendMigrate.py | 139 +++- tools/python/xen/xend/packing.py | 329 ++++++++ tools/xfrd/Make.xfrd | 34 + tools/xfrd/Makefile | 68 ++ tools/xfrd/connection.c | 163 ++++ tools/xfrd/connection.h | 32 + tools/xfrd/debug.h | 72 ++ tools/xfrd/enum.c | 61 ++ tools/xfrd/enum.h | 30 + tools/xfrd/hash_table.c | 640 +++++++++++++++ tools/xfrd/hash_table.h | 294 +++++++ tools/xfrd/lexis.c | 93 +++ tools/xfrd/lexis.h | 127 +++ tools/xfrd/lzi_stream.c | 533 ++++++++++++ tools/xfrd/lzi_stream.h | 35 + tools/xfrd/marshal.c | 207 +++++ tools/xfrd/marshal.h | 42 + tools/xfrd/select.c | 50 ++ tools/xfrd/select.h | 16 + tools/xfrd/sxpr.c | 950 ++++++++++++++++++++++ tools/xfrd/sxpr.h | 414 ++++++++++ tools/xfrd/xdr.c | 316 ++++++++ tools/xfrd/xdr.h | 30 + tools/xfrd/xen_domain.c | 90 ++ tools/xfrd/xen_domain.h | 15 + tools/xfrd/xfrd.c | 1127 ++++++++++++++++++++++++++ tools/xfrd/xfrd.h | 14 + tools/xfrd/xfrdClient.py | 127 +++ 30 files changed, 6070 insertions(+), 52 deletions(-) create mode 100644 tools/python/xen/xend/packing.py create mode 100644 tools/xfrd/Make.xfrd create mode 100644 tools/xfrd/Makefile create mode 100644 tools/xfrd/connection.c create mode 100644 tools/xfrd/connection.h create mode 100644 tools/xfrd/debug.h create mode 100644 tools/xfrd/enum.c create mode 100644 tools/xfrd/enum.h create mode 100644 tools/xfrd/hash_table.c create mode 100644 tools/xfrd/hash_table.h create mode 100644 tools/xfrd/lexis.c create mode 100644 tools/xfrd/lexis.h create mode 100644 tools/xfrd/lzi_stream.c create mode 100644 tools/xfrd/lzi_stream.h create mode 100644 tools/xfrd/marshal.c create mode 100644 tools/xfrd/marshal.h create mode 100644 tools/xfrd/select.c create mode 100644 tools/xfrd/select.h create mode 100644 tools/xfrd/sxpr.c create mode 100644 tools/xfrd/sxpr.h create mode 100644 tools/xfrd/xdr.c create mode 100644 tools/xfrd/xdr.h create mode 100644 tools/xfrd/xen_domain.c create mode 100644 tools/xfrd/xen_domain.h create mode 100644 tools/xfrd/xfrd.c create mode 100644 tools/xfrd/xfrd.h create mode 100755 tools/xfrd/xfrdClient.py diff --git a/.rootkeys b/.rootkeys index ef35788131..88e79ce75d 100644 --- a/.rootkeys +++ b/.rootkeys @@ -241,6 +241,7 @@ 40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/python/xen/xend/XendVnet.py 40c9c468x191zetrVlMnExfsQWHxIQ tools/python/xen/xend/__init__.py 40c9c468S2YnCEKmk4ey8XQIST7INg tools/python/xen/xend/encode.py +40e9808elkoRulOo1GxRTp5ulJGVNw tools/python/xen/xend/packing.py 40c9c468DCpMe542varOolW1Xc68ew tools/python/xen/xend/server/SrvBase.py 40c9c468IxQabrKJSWs0aEjl-27mRQ tools/python/xen/xend/server/SrvConsole.py 40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/python/xen/xend/server/SrvConsoleDir.py @@ -276,6 +277,32 @@ 403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c 403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format 4050c413NtuyIq5lsYJV4P7KIjujXw tools/xentrace/xentrace_format.1 +40e9808eHO3QprCFKg9l2JJzgt2voA tools/xfrd/Make.xfrd +40e9808epTR4zWrYjGUnaaynK20Q5A tools/xfrd/Makefile +40e9808eysqT4VNDlJFqsZB2rdg4Qw tools/xfrd/connection.c +40e9808eyXfJUi4E0C3WSgrEXqQ1sQ tools/xfrd/connection.h +40e9808eULGwffNOE4kBrAfZ9YAVMA tools/xfrd/debug.h +40e9808eyjiahG5uF6AMelNVujBzCg tools/xfrd/enum.c +40e9808eZpbdn9q2KSSMGCNvY_ZgpQ tools/xfrd/enum.h +40e9808easXCzzAZQodEfKAhgUXSPA tools/xfrd/hash_table.c +40e9808e94BNXIVVKBFHC3rnkvwtJg tools/xfrd/hash_table.h +40e9808epW9iHcLXuO3QfUfLzB7onw tools/xfrd/lexis.c +40e9808egccMhCizayQRGtpBA3L5MQ tools/xfrd/lexis.h +40e9808ePADCSKL1YgGCt2TbYPnYkw tools/xfrd/lzi_stream.c +40e9808eDNAdpF71o5teYb9DTT-PRw tools/xfrd/lzi_stream.h +40e9808eQxi0EzTcPJtosrzxEIjA-Q tools/xfrd/marshal.c +40e9808etg13xfRm0Lqd8vY-jHOoTg tools/xfrd/marshal.h +40e9808eCsmywryb036TdtRMJHDMmQ tools/xfrd/select.c +40e9808e99OcM547cKMTfmCVSoWVAw tools/xfrd/select.h +40e9808e5_PLdodqVOSx0b4T_f5aeg tools/xfrd/sxpr.c +40e9808e0O4sHZtkDv5hlSqjYcdQAQ tools/xfrd/sxpr.h +40e9808eF3NVldqRNS5IHM8gbFAvpw tools/xfrd/xdr.c +40e9808ezXzoRHm7pybXU69NtnjimA tools/xfrd/xdr.h +40e9808edpUtf4bJ8IbqClPJj_OvbA tools/xfrd/xen_domain.c +40e9808eHviFFIwdUKOA234uIeifjA tools/xfrd/xen_domain.h +40e9808eIFeV-MDCNyVTNt5NfMPKeQ tools/xfrd/xfrd.c +40e9808eGIbOoSNJRiwWK2C3mjGWaA tools/xfrd/xfrd.h +40e9808eHXvs_5eggj9McD_J90mhNw tools/xfrd/xfrdClient.py 3f72f1bdJPsV3JCnBqs9ddL9tr6D2g xen/COPYING 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen/Makefile 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen/Rules.mk diff --git a/tools/libxutil/string_stream.c b/tools/libxutil/string_stream.c index c3cf423d84..495238d74a 100644 --- a/tools/libxutil/string_stream.c +++ b/tools/libxutil/string_stream.c @@ -25,8 +25,6 @@ #include "string_stream.h" #include "allocate.h" -static int string_print(IOStream *io, const char *msg, va_list args); -static int string_getc(IOStream *io); static int string_error(IOStream *io); static int string_close(IOStream *io); static void string_free(IOStream *io); @@ -49,45 +47,6 @@ static inline StringData *get_string_data(IOStream *io){ return (StringData*)io->data; } -/** Read a character from a string stream. - * - * @param io string stream - * @return character read, IOSTREAM_EOF if no more input - */ -static int string_getc(IOStream *io){ - StringData *data = get_string_data(io); - int c = IOSTREAM_EOF; - char *s = data->in; - - if(s && s < data->end){ - c = (unsigned)*s; - data->in = s+1; - } - return c; -} - -/** Print to a string stream. - * Formats the data to an internal buffer and prints it. - * The formatted data must fit into the internal buffer. - * - * @param io string stream - * @param format print format - * @param args print arguments - * @return result of the print - */ -static int string_print(IOStream *io, const char *msg, va_list args){ - StringData *data = get_string_data(io); - int k = data->end - data->out; - int n = vsnprintf(data->out, k, (char*)msg, args); - if(n < 0 || n > k ){ - n = k; - IOStream_close(io); - } else { - data->out += n; - } - return n; -} - /** Test if a string stream has an error. * * @param io string stream @@ -118,7 +77,7 @@ static int string_close(IOStream *io){ */ static void string_free(IOStream *io){ StringData *data = get_string_data(io); - zero(data, sizeof(*data)); + memzero(data, sizeof(*data)); deallocate(data); } @@ -139,13 +98,13 @@ IOMethods *string_stream_get_methods(void){ */ void string_stream_init(IOStream *io, StringData *data, char *s, int n){ if(data && io){ - zero(data, sizeof(*data)); + memzero(data, sizeof(*data)); data->string = (char*)s; data->in = data->string; data->out = data->string; data->size = n; data->end = data->string + n; - zero(io, sizeof(*io)); + memzero(io, sizeof(*io)); io->methods = &string_methods; io->data = data; } diff --git a/tools/python/xen/xend/XendMigrate.py b/tools/python/xen/xend/XendMigrate.py index 1580ba83ed..471ea7d1dd 100644 --- a/tools/python/xen/xend/XendMigrate.py +++ b/tools/python/xen/xend/XendMigrate.py @@ -2,22 +2,124 @@ import sys import socket +import time + +from twisted.internet import reactor +from twisted.internet import defer +from twisted.internet.protocol import Protocol +from twisted.internet.protocol import ClientFactory import sxp import XendDB import EventServer; eserver = EventServer.instance() +from xen.xend.packing import SxpPacker, SxpUnpacker +from xen.xend import XendDomain +xd = XendDomain.instance() + + +XFRD_PORT = 8002 + +XFR_PROTO_MAJOR = 1 +XFR_PROTO_MINOR = 0 + +class Migrate(Protocol): + + def __init__(self, minfo): + self.packer = None + self.unpacker = None + self.minfo = minfo + + def connectionMade(self): + self.packer = SxpPacker(self.transport) + self.unpacker = SxpPacker() + # Send hello. + self.packer.pack(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR]) + # Send migrate. + vmconfig = self.minfo.vmconfig() + if not vmconfig: + self.loseConnection() + return + self.packer.pack(['xfr.migrate', + self.minfo.src_dom, + vmconfig, + self.minfo.dst_host, + self.minfo.dst_port]) + + def connectionLost(self, reason): + self.minfo.closed(reason) + + def dataReceived(self, data): + try: + self.unpacker.reset(data) + val = self.unpacker.unpack() + print 'dataReceived>', 'val=', val + op = val[0] + op.replace('.', '_') + if op.startwith('xfr_'): + fn = getattr(self, op, self.unknown) + else: + fn = self.unknown + fn(val) + except Exception, ex: + print 'dataReceived>', ex + pass + + def unknown(self, val): + print 'unknown>', val + + def xfr_progress(self, val): + print 'xfr_progress>', val + + def xfr_error(self, val): + # If we get an error with non-zero code the migrate failed. + # An error with code zero indicates hello success. + err = int(val[1]) + if not err: return + self.minfo.error(err); + self.loseConnection() + + def xfr_ok(self, val): + # An ok indicates migrate completed successfully, and contains + # the new domain id on the remote system. + dom = int(val[1]) + self.minfo.ok(dom) + self.loseConnection() + +class MigrateClientFactory(ClientFactory): + + def __init__(self, minfo): + ClientFactory.__init__(self) + self.minfo = minfo + + def startedConnecting(self, connector): + print 'Started to connect', 'self=', self, 'connector=', connector + + def buildProtocol(self, addr): + print 'buildProtocol>', addr + return Migrate(self.minfo) + + def clientConnectionLost(self, connector, reason): + print 'clientConnectionLost>', 'connector=', connector, 'reason=', reason + + def clientConnectionFailed(self, connector, reason): + print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason + + class XendMigrateInfo: # states: begin, active, failed, succeeded? - def __init__(self, id, dom, dst): + def __init__(self, id, dom, host, port): self.id = id self.state = 'begin' self.src_host = socket.gethostname() self.src_dom = dom self.dst_host = dst + self.dst_port = port self.dst_dom = None + self.start = 0 + self.deferred = defer.Deferred() def set_state(self, state): self.state = state @@ -29,12 +131,33 @@ class XendMigrateInfo: sxpr = ['migrate', ['id', self.id], ['state', self.state] ] sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ] sxpr.append(sxpr_src) - sxpr_dst = ['dst', ['host', self.dst] ] + sxpr_dst = ['dst', ['host', self.dst_host] ] if self.dst_dom: sxpr_dst.append(['domain', self.dst_dom]) sxpr.append(sxpr_dst) return sxpr - + + def vmconfig(self): + dominfo = xd.domain_get(self.dom) + if dominfo: + val = return sxp.to_string(dominfo) + else: + val = None + return None + + def error(self, err): + self.state = 'error' + + def ok(self, dom): + self.state = 'ok' + self.dst_dom = dom + + def close(self): + if self.state =='ok': + eserver.inject('xend.migrate.ok', self.sxpr()) + else: + self.state = 'error' + eserver.inject('xend.migrate.error', self.sxpr()) class XendMigrate: # Represents migration in progress. @@ -84,15 +207,15 @@ class XendMigrate: def migrate_get(self, id): return self.migrate.get(id) - def migrate_begin(self, dom, dst): + def migrate_begin(self, dom, host): # Check dom for existence, not migrating already. - # Create migrate info, tell xend to migrate it? - # - or fork migrate command ourselves? # Subscribe to migrate notifications (for updating). id = self.nextid() - info = XenMigrateInfo(id, dom, dst) + info = XenMigrateInfo(id, dom, host, XFRD_PORT) self._add_migrate(id, info) - return id + mcf = MigrateClientFactory(info) + reactor.connectTCP('localhost', XFRD_PORT, mcf) + return info.deferred def instance(): global inst diff --git a/tools/python/xen/xend/packing.py b/tools/python/xen/xend/packing.py new file mode 100644 index 0000000000..760af7f5a1 --- /dev/null +++ b/tools/python/xen/xend/packing.py @@ -0,0 +1,329 @@ + +# XDR-style packer/unpacker for sxpr. +# +# string -> [STRING] [len:u16] +# atom -> [ATOM] [len:u16] +# int -> [UINT] [value] +# list -> [LIST] {1 elt}* 0 +# null -> [NULL] +# none -> [NONE] +# bool -> [BOOL] { 0:u8 | 1:u8 } +# +# types packed as u16. +# +# So (a b c) -> [LIST] 1 a 1 b 1 c 0 +# () -> [LIST] 0 + +import struct + +try: + from cStringIO import StringIO as _StringIO +except ImportError: + from StringIO import StringIO as _StringIO + +import types + +class Error(Exception): + + def __init__(self, msg): + self.msg = msg + + def __repr__(self): + return repr(self.msg) + + def __str__(self): + return str(self.msg) + + +class ConversionError(Error): + pass + +BOOL_SIZE = 1 +BOOL_FMT = '>B' + +BYTE_SIZE = 1 +BYTE_FMT = '>b' +UBYTE_FMT = '>B' + +SHORT_SIZE = 2 +SHORT_FMT = '>h' +USHORT_FMT = '>H' + +INT_SIZE = 4 +INT_FMT = '>l' +UINT_FMT = '>L' + +NONE_CODE = 0 +NULL_CODE = 1 +INT_CODE = 2 +STRING_CODE = 3 +ATOM_CODE = 4 +BOOL_CODE = 5 +LIST_CODE = 10 + +class Packer: + + def __init__(self, io=None): + self.reset(io=io) + + def reset(self, io=None): + if io is None: + io = _StringIO() + self.io = io + + def get_buffer(self): + return self.io.getvalue() + + def get_io(self): + return self.io + + def struct_pack(self, fmt, x): + try: + self.io.write(struct.pack(fmt, x)) + except struct.error, msg: + raise ConversionError, msg + + def pack_none(self): + pass + + def pack_bool(self, x): + # { '1' | '0' } + print 'bool>', x + if x: + self.io.write('\1') + else: + self.io.write('\0') + + def pack_byte(self, x): + self.struct_pack(BYTE_FMT, x & 0xff) + + def pack_char(self, x): + print 'char>', x + self.io.write(x) + + def pack_ubyte(self, x): + print 'ubyte>', x + self.struct_pack(UBYTE_FMT, x & 0xff) + + def pack_ushort(self, x): + print 'ushort>', x + self.struct_pack(USHORT_FMT, x & 0xffff) + + def pack_short(self, x): + print 'short>', x + self.struct_pack(SHORT_FMT, x & 0xffff) + + def pack_uint(self, x): + print 'uint>', x + self.struct_pack(UINT_FMT, x) + + def pack_int(self, x): + print 'int>', x + self.struct_pack(INT_FMT, x) + + def pack_uhyper(self, x): + print 'uhyper>', x + self.pack_uint(x>>32 & 0xffffffffL) + self.pack_uint(x & 0xffffffffL) + + pack_hyper = pack_uhyper + + def pack_fstring(self, n, x): + print 'fstring>', x + self.io.write(x) + + pack_fopaque = pack_fstring + + def pack_string(self, x): + print 'string>', x + n = len(x) + self.pack_ushort(n) + self.pack_fstring(n, x) + + pack_opaque = pack_string + pack_bytes = pack_string + + def pack_list(self, x, pack_item): + print 'list>', x + # { '1' }* '0' + for item in x: + self.pack_bool(1) + pack_item(item) + self.pack_bool(0) + + def pack_farray(self, x, pack_item): + # * + # Can pass n and check length - but is it worth it? + print 'farray>', list + for item in x: + pack_item(item) + + def pack_array(self, x, pack_item): + # n *n + print 'array>', x + self.pack_uint(len(x)) + self.pack_farray(x, pack_item) + +class Unpacker: + + def __init__(self, data): + self.reset(data) + + def reset(self, data): + if isinstance(data, types.StringType): + data = _StringIO(data) + self.io = data + + def get_bytes(self, n): + if n < 0: + raise ConversionError('negative byte count') + data = self.io.read(n) + return data + + def struct_unpack(self, fmt, n): + data = self.get_bytes(n) + try: + return struct.unpack(fmt, data)[0] + except struct.error, msg: + raise ConversionError, msg + + def unpack_none(self): + return None + + def unpack_bool(self): + return self.struct_unpack(BOOL_FMT, BOOL_SIZE) + + def unpack_char(self): + return self.get_bytes(1)[0] + + def unpack_byte(self): + return self.struct_unpack(BYTE_FMT, BYTE_SIZE) + + def unpack_ubyte(self): + return self.struct_unpack(UBYTE_FMT, BYTE_SIZE) + + def unpack_ushort(self): + return self.struct_unpack(USHORT_FMT, SHORT_SIZE) + + def unpack_short(self): + return self.struct_unpack(SHORT_FMT, SHORT_SIZE) + + def unpack_uint(self): + x = self.struct_unpack(UINT_FMT, UINT_SIZE) + try: + return int(x) + except OverflowError: + return x + + def unpack_int(self): + return self.struct_unpack(INT_FMT, INT_SIZE) + + def unpack_uhyper(self): + hi = self.unpack_uint() + lo = self.unpack_uint() + return long(hi)<<32 | lo + + def unpack_hyper(self): + x = self.unpack_uhyper() + if x >= 0x8000000000000000L: + x = x - 0x10000000000000000L + return x + + def unpack_fstring(self, n): + return self.get_bytes(n) + + unpack_fopaque = unpack_fstring + + def unpack_string(self): + n = self.unpack_ushort() + return self.unpack_fstring(n) + + unpack_opaque = unpack_string + unpack_bytes = unpack_string + + def unpack_list(self, unpack_item): + list = [] + while self.unpack_bool(): + list.append(unpack_item()) + return list + + def unpack_farray(self, n, unpack_item): + list = [] + for i in range(n): + list.append(unpack_item()) + return list + + def unpack_array(self, unpack_item): + n = self.unpack_ushort() + return self.unpack_farray(n, unpack_item) + +class SxpPacker(Packer): + + pack_code = Packer.pack_ushort + + def pack(self, x): + if isinstance(x, types.NoneType): + self.pack_code(NONE_CODE) + self.pack_none() + elif isinstance(x, types.IntType): + self.pack_code(INT_CODE) + self.pack_int(x) + elif isinstance(x, types.StringType): + self.pack_code(STRING_CODE) + self.pack_string(x) + elif isinstance(x, types.ListType): + self.pack_code(LIST_CODE) + self.pack_list(x, self.pack) + else: + raise Error('invalid type ' + str(type(x))) + +class SxpUnpacker(Unpacker): + + unpack_code = Unpacker.unpack_ushort + + def unpack(self): + code = self.unpack_code() + if code == NONE_CODE: + val = self.unpack_none() + elif code == INT_CODE: + val = self.unpack_int() + elif code == BOOL_CODE: + val = self.unpack_bool() + elif code == STRING_CODE: + val = self.unpack_string() + elif code == ATOM_CODE: + val = self.unpack_string() + elif code == LIST_CODE: + val = self.unpack_list(self.unpack) + else: + raise Error('invalid code ' + str(code)) + return val + +def main(): + d = "['vfarm', ['@', ['name', 'vfarm1']], ['memory', 1024], ['image', 'splinux'], ['args', 'root=/dev/nfs ip=dhcp'], [ 1, -1, 1000000]]" + print"> len=", len(d), "d=", d + obj = ['vfarm', ['@', ['name', 'vfarm1']], + ['memory', 1024], + ['image', 'splinux'], + ['args', 'root=/dev/nfs ip=dhcp'], + [ 1, -1, 1000000] ] + print "> obj=", obj + pack = SxpPacker() + pack.pack(obj) + data = pack.get_buffer() + print "> len=", len(data), "data=", data + unpack = SxpUnpacker(data) + obj_unpack = unpack.unpack() + print "> obj=", obj_unpack + #obj = [100,101,102, 999.00234, { 'a': 1, 'b': 2 } ] + #pack.reset() + #pack.pack_item(obj) + #data = pack.get_buffer() + #print "> obj=", obj + #print "> len=", len(data), "data=", data + #unpack.reset(data) + #obj_unpack = unpack.unpack_item() + #print "> obj=", obj_unpack + +if __name__ == "__main__": + main() diff --git a/tools/xfrd/Make.xfrd b/tools/xfrd/Make.xfrd new file mode 100644 index 0000000000..39f3afd3c5 --- /dev/null +++ b/tools/xfrd/Make.xfrd @@ -0,0 +1,34 @@ +# -*- mode: Makefile; -*- +#============================================================================ + +UTIL_LIB = libutil.a + +UTIL_LIB_SRC = +UTIL_LIB_SRC += allocate.c +UTIL_LIB_SRC += enum.c +UTIL_LIB_SRC += file_stream.c +UTIL_LIB_SRC += gzip_stream.c +UTIL_LIB_SRC += hash_table.c +UTIL_LIB_SRC += iostream.c +UTIL_LIB_SRC += lexis.c +UTIL_LIB_SRC += lzi_stream.c +UTIL_LIB_SRC += marshal.c +UTIL_LIB_SRC += string_stream.c +UTIL_LIB_SRC += sxpr.c +#UTIL_LIB_SRC += sxpr_parser.c +UTIL_LIB_SRC += sys_net.c +UTIL_LIB_SRC += sys_string.c +#UTIL_LIB_SRC += util.c +UTIL_LIB_SRC += xdr.c + +#---------------------------------------------------------------------------- +# Xfrd. + +XFRD_PROG_SRC = +XFRD_PROG_SRC += xfrd.c +#XFRD_PROG_SRC += xfr_msg.c +XFRD_PROG_SRC += xen_domain.c +XFRD_PROG_SRC += select.c +XFRD_PROG_SRC += connection.c + +#============================================================================ diff --git a/tools/xfrd/Makefile b/tools/xfrd/Makefile new file mode 100644 index 0000000000..fe84120471 --- /dev/null +++ b/tools/xfrd/Makefile @@ -0,0 +1,68 @@ +# -*- mode: Makefile; -*- +#============================================================================ +# +# Mike Wray +#============================================================================ + +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Make.defs + +XFRD_INSTALL_DIR = /usr/sbin + +vpath %.h $(XEN_HYPERVISOR_IFS) +INCLUDES += -I $(XEN_HYPERVISOR_IFS) + +vpath %h $(XEN_LINUX_INCLUDE) +INCLUDES += -I $(XEN_LINUX_INCLUDE) + +vpath %.h $(XEN_XU) +INCLUDES += -I $(XEN_XU) + +vpath %c $(XEN_LIBXUTIL) +INCLUDES += -I $(XEN_LIBXUTIL) + +include Make.xfrd + +UTIL_LIB_OBJ = $(UTIL_LIB_SRC:.c=.o) + +XFRD_PROG_OBJ = $(XFRD_PROG_SRC:.c=.o) +XFRD_PROG_OBJ += $(UTIL_LIB) + +CPPFLAGS += -D _XEN_XFR_STUB_ + +CFLAGS += -g +CFLAGS += -Wall +CFALGS += -Werror +CFLAGS += $(INCLUDES) +# Make gcc generate dependencies. +CFLAGS += -Wp,-MD,.$(@F).d +PROG_DEP = .*.d + +#LDFLAGS += -L $(COMPRESS_DIR) -lz + +$(warning XFRD_PROG_OBJ= $(XFRD_PROG_OBJ)) +$(warning UTIL_LIB= $(UTIL_LIB)) +$(warning UTIL_LIB_OBJ= $(UTIL_LIB_OBJ)) + +all: xfrd + +xfrd: $(XFRD_PROG_OBJ) -lz + +.PHONY: install +install: xfrd + mkdir -p $(prefix)/$(XFRD_INSTALL_DIR) + install -m 0755 xfrd $(prefix)/$(XFRD_INSTALL_DIR) + +.PHONY: libutil +libutil: $(UTIL_LIB) + +$(UTIL_LIB): $(UTIL_LIB_OBJ) + $(AR) rc $@ $^ + +.PHONY: clean +clean: + $(RM) *.o *.a *.so *~ xfrd + $(RM) $(PROG_DEP) + +-include $(PROG_DEP) + diff --git a/tools/xfrd/connection.c b/tools/xfrd/connection.c new file mode 100644 index 0000000000..26dca9968b --- /dev/null +++ b/tools/xfrd/connection.c @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include + +#include "connection.h" +#include "file_stream.h" +#include "lzi_stream.h" + +#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WARN] %s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stdout, "[INFO] %s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] %s" fmt, __FUNCTION__, ##args) + +/** Compress magic header. */ +char compress_magic[2] = { 0x1f, 0x8b }; + +/** Plain magic header. */ +char plain_magic[2] = { 0x0, 0x0 }; + +int Conn_read_header(int sock, int *flags){ + int err = 0; + char magic[2] = {}; + int k, n = sizeof(magic); + k = read(sock, magic, n); + if(k != n){ + err = -EINVAL; + goto exit; + } + dprintf("> magic={ 0x%x, 0x%x }\n", magic[0], magic[1]); + if(magic[0] == compress_magic[0] && magic[1] == compress_magic[1]){ + *flags |= CONN_READ_COMPRESS; + dprintf("> Using compress read.\n"); + } else { + dprintf("> Using plain read.\n"); + } + exit: + return err; +} + +int Conn_write_header(int sock, int flags){ + int err = 0; + if(flags & CONN_WRITE_COMPRESS){ + dprintf("> Using compress write.\n"); + err = write(sock, compress_magic, 2); + } else { + dprintf("> Using plain write.\n"); + err = write(sock, plain_magic, 2); + } + if(err == 2) err = 0; + return err; +} + +/** Initialize a file stream from a file desciptor. + * + * @param fd file descriptor + * @param mode file mode + * @param flags control compression and buffering + * @param io return parameter for the stream + * @return 0 on success, error code otherwise + */ +int stream_init(int fd, const char *mode, int flags, int compress, IOStream **io){ + int err = 0; + dprintf(">mode=%s flags=%x compress=%d\n", mode, flags, compress); + if(compress){ + *io = lzi_stream_fdopen(fd, mode); + } else { + *io = file_stream_fdopen(fd, mode); + } + if(!*io){ + err = -errno; + perror("fdopen"); + goto exit; + } + if(1 && (flags & CONN_NOBUFFER)){ + // Make unbuffered. + dprintf("> unbuffer...\n"); + err = file_stream_setvbuf((compress ? lzi_stream_io(*io) : *io), NULL, _IONBF, 0); + if(err){ + err = -errno; + perror("setvbuf"); + goto exit; + } + } + exit: + if(err && *io){ + dprintf("> close err=%d\n", err); + IOStream_close(*io); + *io = NULL; + } + dprintf("< err=%d\n", err); + return err; +} + +/** Initialize a connection. + * + * @param conn connection + * @param flags + * @param sock socket + * @param ipaddr ip address + * @return 0 on success, error code otherwise + */ +int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr){ + int err = 0; + dprintf("> flags=%x\n", flags); + conn->addr = addr; + conn->sock = sock; + dprintf("> write stream...\n"); + err = stream_init(sock, "w", flags, (flags & CONN_WRITE_COMPRESS), &conn->out); + if(err) goto exit; + IOStream_flush(conn->out); + dprintf("> read stream...\n"); + err = stream_init(sock, "r", flags, (flags & CONN_READ_COMPRESS) , &conn->in); + if(err) goto exit; + exit: + if(err) eprintf("< err=%d\n", err); + return err; +} + +/** Open a connection. + * + * @param conn connection + * @param flags + * @param ipaddr ip address to connect to + * @param port port + * @return 0 on success, error code otherwise + */ +int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port){ + int err = 0; + int sock; + struct sockaddr_in addr_in; + struct sockaddr *addr = (struct sockaddr *)&addr_in; + socklen_t addr_n = sizeof(addr_in); + dprintf("> addr=%s:%d\n", inet_ntoa(ipaddr), ntohs(port)); + sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0){ + err = -errno; + goto exit; + } + addr_in.sin_family = AF_INET; + addr_in.sin_addr = ipaddr; + addr_in.sin_port = port; + err = connect(sock, addr, addr_n); + if(err) goto exit; + //err = Conn_write_header(sock, flags); + //if(err < 0) goto exit; + err = Conn_init(conn, flags, sock, addr_in); + exit: + if(err) eprintf("< err=%d\n", err); + return err; +} + +/** Close a connection. + * + * @param conn connection + */ +void Conn_close(Conn *conn){ + if(conn->in) IOStream_close(conn->in); + if(conn->out) IOStream_close(conn->out); + shutdown(conn->sock, 2); +} diff --git a/tools/xfrd/connection.h b/tools/xfrd/connection.h new file mode 100644 index 0000000000..790c01f1e8 --- /dev/null +++ b/tools/xfrd/connection.h @@ -0,0 +1,32 @@ +/* $Id: connection.h,v 1.1 2003/10/17 15:48:43 mjw Exp $ */ +#ifndef _VFC_CONNECTION_H_ +#define _VFC_CONNECTION_H_ + +#include + +#include "iostream.h" + +/** A connection. + * The underlying transport is a socket. + * Contains in and out streams using the socket. + */ +typedef struct Conn { + struct sockaddr_in addr; + int sock; + IOStream *in; + IOStream *out; +} Conn; + +enum { + CONN_NOBUFFER=1, + CONN_READ_COMPRESS=2, + CONN_WRITE_COMPRESS=4, +}; + +extern int Conn_read_header(int sock, int *flags); +extern int Conn_write_header(int sock, int flags); +extern int Conn_init(Conn *conn, int flags, int sock, struct sockaddr_in addr); +extern int Conn_connect(Conn *conn, int flags, struct in_addr ipaddr, uint16_t port); +extern void Conn_close(Conn *conn); + +#endif /* ! _VFC_CONNECTION_H_ */ diff --git a/tools/xfrd/debug.h b/tools/xfrd/debug.h new file mode 100644 index 0000000000..69a6b49937 --- /dev/null +++ b/tools/xfrd/debug.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _XUTIL_DEBUG_H_ +#define _XUTIL_DEBUG_H_ + +#ifndef MODULE_NAME +#define MODULE_NAME "" +#endif + +#ifdef __KERNEL__ +#include +#include + +#ifdef DEBUG + +#define dprintf(fmt, args...) printk(KERN_DEBUG "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) + +#else + +#define dprintf(fmt, args...) do {} while(0) +#define wprintf(fmt, args...) printk(KERN_WARNING "[WRN] " MODULE_NAME fmt, ##args) +#define iprintf(fmt, args...) printk(KERN_INFO "[INF] " MODULE_NAME fmt, ##args) +#define eprintf(fmt, args...) printk(KERN_ERR "[ERR] " MODULE_NAME fmt, ##args) + +#endif + +#else + +#include + +#ifdef DEBUG + +#define dprintf(fmt, args...) fprintf(stdout, "[DBG] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME ">%s" fmt, __FUNCTION__, ##args) + +#else + +#define dprintf(fmt, args...) do {} while(0) +#define wprintf(fmt, args...) fprintf(stderr, "[WRN] " MODULE_NAME fmt, ##args) +#define iprintf(fmt, args...) fprintf(stderr, "[INF] " MODULE_NAME fmt, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERR] " MODULE_NAME fmt, ##args) + +#endif + +#endif + +/** Print format for an IP address. + * See NIPQUAD(), HIPQUAD() + */ +#define IPFMT "%u.%u.%u.%u" + +#endif /* ! _XUTIL_DEBUG_H_ */ diff --git a/tools/xfrd/enum.c b/tools/xfrd/enum.c new file mode 100644 index 0000000000..95f6e31a87 --- /dev/null +++ b/tools/xfrd/enum.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2002, 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include "sys_string.h" +#include "enum.h" + +/** Map an enum name to its value using a table. + * + * @param name enum name + * @param defs enum definitions + * @return enum value or -1 if not known + */ +int enum_name_to_val(char *name, EnumDef *defs){ + int val = -1; + for(; defs->name; defs++){ + if(!strcmp(defs->name, name)){ + val = defs->val; + break; + } + } + return val; +} + +/** Map an enum value to its name using a table. + * + * @param val enum value + * @param defs enum definitions + * @param defs_n number of definitions + * @return enum name or NULL if not known + */ +char *enum_val_to_name(int val, EnumDef *defs){ + char *name = NULL; + for(; defs->name; defs++){ + if(val == defs->val){ + name = defs->name; + break; + } + } + return name; +} + diff --git a/tools/xfrd/enum.h b/tools/xfrd/enum.h new file mode 100644 index 0000000000..cdc0f6f1b1 --- /dev/null +++ b/tools/xfrd/enum.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2002, 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_ENUM_H_ +#define _XUTIL_ENUM_H_ + +/** Mapping of an enum value to a name. */ +typedef struct EnumDef { + int val; + char *name; +} EnumDef; + +extern int enum_name_to_val(char *name, EnumDef *defs); +extern char *enum_val_to_name(int val, EnumDef *defs); + +#endif /* _XUTIL_ENUM_H_ */ diff --git a/tools/xfrd/hash_table.c b/tools/xfrd/hash_table.c new file mode 100644 index 0000000000..13da946e77 --- /dev/null +++ b/tools/xfrd/hash_table.c @@ -0,0 +1,640 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef __KERNEL__ +# include +# include +# include +# include +#else +# include +# include +#endif + +//#include + +#include "allocate.h" +#include "hash_table.h" + +/** @file + * Base support for hashtables. + * + * Hash codes are reduced modulo the number of buckets to index tables, + * so there is no need for hash functions to limit the range of hashcodes. + * In fact it is assumed that hashcodes do not change when the number of + * buckets in the table changes. + */ + +/*==========================================================================*/ +/** Number of bits in half a word. */ +//#if __WORDSIZE == 64 +//#define HALF_WORD_BITS 32 +//#else +#define HALF_WORD_BITS 16 +//#endif + +/** Mask for lo half of a word. On 32-bit this is + * (1<<16) - 1 = 65535 = 0xffff + * It's 4294967295 = 0xffffffff on 64-bit. + */ +#define LO_HALF_MASK ((1 << HALF_WORD_BITS) - 1) + +/** Get the lo half of a word. */ +#define LO_HALF(x) ((x) & LO_HALF_MASK) + +/** Get the hi half of a word. */ +#define HI_HALF(x) ((x) >> HALF_WORD_BITS) + +/** Do a full hash on both inputs, using DES-style non-linear scrambling. + * Both inputs are replaced with the results of the hash. + * + * @param pleft input/output word + * @param pright input/output word + */ +void pseudo_des(unsigned long *pleft, unsigned long *pright){ + // Bit-rich mixing constant. + static const unsigned long a_mixer[] = { + 0xbaa96887L, 0x1e17d32cL, 0x03bcdc3cL, 0x0f33d1b2L, }; + + // Bit-rich mixing constant. + static const unsigned long b_mixer[] = { + 0x4b0f3b58L, 0xe874f0c3L, 0x6955c5a6L, 0x55a7ca46L, }; + + // Number of iterations - must be 2 or 4. + static const int ncycle = 4; + //static const int ncycle = 2; + + unsigned long left = *pleft, right = *pright; + unsigned long v, v_hi, v_lo; + int i; + + for(i=0; ibuckets + (hashcode % table->buckets_n); +} + +/** Initialize a hash table. + * Can be safely called more than once. + * + * @param table to initialize + */ +void HashTable_init(HashTable *table){ + int i; + + if(!table->init_done){ + table->init_done = 1; + table->next_id = 0; + for(i=0; ibuckets_n; i++){ + HTBucket *bucket = get_bucket(table, i); + bucket->head = 0; + bucket->count = 0; + } + table->entry_count = 0; + } +} + +/** Allocate a new hashtable. + * If the number of buckets is not positive the default is used. + * The number of buckets should usually be prime. + * + * @param buckets_n number of buckets + * @return new hashtable or null + */ +HashTable *HashTable_new(int buckets_n){ + HashTable *z = ALLOCATE(HashTable); + if(!z) goto exit; + if(buckets_n <= 0){ + buckets_n = HT_BUCKETS_N; + } + z->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket)); + if(!z->buckets){ + deallocate(z); + z = 0; + goto exit; + } + z->buckets_n = buckets_n; + HashTable_init(z); + exit: + return z; +} + +/** Free a hashtable. + * Any entries are removed and freed. + * + * @param h hashtable (ignored if null) + */ +void HashTable_free(HashTable *h){ + if(h){ + HashTable_clear(h); + deallocate(h->buckets); + deallocate(h); + } +} + +/** Push an entry on the list in the bucket for a given hashcode. + * + * @param table to add entry to + * @param hashcode for the entry + * @param entry to add + */ +static inline void push_on_bucket(HashTable *table, Hashcode hashcode, + HTEntry *entry){ + HTBucket *bucket; + HTEntry *old_head; + + bucket = get_bucket(table, hashcode); + old_head = bucket->head; + bucket->count++; + bucket->head = entry; + entry->next = old_head; +} + +/** Change the number of buckets in a hashtable. + * No-op if the number of buckets is not positive. + * Existing entries are reallocated to buckets based on their hashcodes. + * The table is unmodified if the number of buckets cannot be changed. + * + * @param table hashtable + * @param buckets_n new number of buckets + * @return 0 on success, error code otherwise + */ +int HashTable_set_buckets_n(HashTable *table, int buckets_n){ + int err = 0; + HTBucket *old_buckets = table->buckets; + int old_buckets_n = table->buckets_n; + int i; + + if(buckets_n <= 0){ + err = -EINVAL; + goto exit; + } + table->buckets = (HTBucket*)allocate(buckets_n * sizeof(HTBucket)); + if(!table->buckets){ + err = -ENOMEM; + table->buckets = old_buckets; + goto exit; + } + table->buckets_n = buckets_n; + for(i=0; ihead; entry; entry = next){ + next = entry->next; + push_on_bucket(table, entry->hashcode, entry); + } + } + deallocate(old_buckets); + exit: + return err; +} + +/** Adjust the number of buckets so the table is neither too full nor too empty. + * The table is unmodified if adjusting fails. + * + * @param table hash table + * @param buckets_min minimum number of buckets (use default if 0 or negative) + * @return 0 on success, error code otherwise + */ +int HashTable_adjust(HashTable *table, int buckets_min){ + int buckets_n = 0; + int err = 0; + if(buckets_min <= 0) buckets_min = HT_BUCKETS_N; + if(table->entry_count >= table->buckets_n){ + // The table is dense - expand it. + buckets_n = 2 * table->buckets_n; + } else if((table->buckets_n > buckets_min) && + (4 * table->entry_count < table->buckets_n)){ + // The table is more than minimum size and sparse - shrink it. + buckets_n = 2 * table->entry_count; + if(buckets_n < buckets_min) buckets_n = buckets_min; + } + if(buckets_n){ + err = HashTable_set_buckets_n(table, buckets_n); + } + return err; +} + +/** Allocate a new entry for a given value. + * + * @param value to put in the entry + * @return entry, or 0 on failure + */ +HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value){ + HTEntry *z = ALLOCATE(HTEntry); + if(z){ + z->hashcode = hashcode; + z->key = key; + z->value = value; + } + return z; +} + +/** Free an entry. + * + * @param z entry to free + */ +inline void HTEntry_free(HTEntry *z){ + if(z){ + deallocate(z); + } +} + +/** Free an entry in a hashtable. + * The table's entry_free_fn is used is defined, otherwise + * the HTEntry itself is freed. + * + * @param table hashtable + * @param entry to free + */ +inline void HashTable_free_entry(HashTable *table, HTEntry *entry){ + if(!entry)return; + if(table && table->entry_free_fn){ + table->entry_free_fn(table, entry); + } else { + HTEntry_free(entry); + } +} + +/** Get the first entry satisfying a test from the bucket for the + * given hashcode. + * + * @param table to look in + * @param hashcode indicates the bucket + * @param test_fn test to apply to elements + * @param arg first argument to calls to test_fn + * @return entry found, or 0 + */ +inline HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg){ + HTBucket *bucket; + HTEntry *entry = 0; + HTEntry *next; + + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(test_fn(arg, table, entry)){ + break; + } + } + return entry; +} + +/** Test hashtable keys for equality. + * Uses the table's key_equal_fn if defined, otherwise pointer equality. + * + * @param key1 key to compare + * @param key2 key to compare + * @return 1 if equal, 0 otherwise + */ +inline int HashTable_key_equal(HashTable *table, void *key1, void *key2){ + return (table->key_equal_fn ? table->key_equal_fn(key1, key2) : key1==key2); +} + +/** Compute the hashcode of a hashtable key. + * The table's key_hash_fn is used if defined, otherwise the address of + * the key is hashed. + * + * @param table hashtable + * @param key to hash + * @return hashcode + */ +inline Hashcode HashTable_key_hash(HashTable *table, void *key){ + return (table->key_hash_fn ? table->key_hash_fn(key) : hash_ul((unsigned long)key)); +} + +/** Test if an entry has a given key. + * + * @param arg containing key to test for + * @param table the entry is in + * @param entry to test + * @return 1 if the entry has the key, 0 otherwise + */ +static inline int has_key(TableArg arg, HashTable *table, HTEntry *entry){ + return HashTable_key_equal(table, arg.ptr, entry->key); +} + +/** Get an entry with a given key. + * + * @param table to search + * @param key to look for + * @return entry if found, null otherwise + */ +#if 0 +inline HTEntry * HashTable_get_entry(HashTable *table, void *key){ + TableArg arg = { ptr: key }; + return HashTable_find_entry(table, HashTable_key_hash(table, key), has_key, arg); +} +#else +inline HTEntry * HashTable_get_entry(HashTable *table, void *key){ + Hashcode hashcode; + HTBucket *bucket; + HTEntry *entry = 0; + HTEntry *next; + + hashcode = HashTable_key_hash(table, key); + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(HashTable_key_equal(table, key, entry->key)){ + break; + } + } + return entry; +} +#endif + +/** Get the value of an entry with a given key. + * + * @param table to search + * @param key to look for + * @return value if an entry was found, null otherwise + */ +inline void * HashTable_get(HashTable *table, void *key){ + HTEntry *entry = HashTable_get_entry(table, key); + return (entry ? entry->value : 0); +} + +/** Print the buckets in a table. + * + * @param table to print + */ +void show_buckets(HashTable *table, IOStream *io){ + int i,j ; + IOStream_print(io, "entry_count=%d buckets_n=%d\n", table->entry_count, table->buckets_n); + for(i=0; ibuckets_n; i++){ + if(0 || table->buckets[i].count>0){ + IOStream_print(io, "bucket %3d %3d %10p ", i, + table->buckets[i].count, + table->buckets[i].head); + for(j = table->buckets[i].count; j>0; j--){ + IOStream_print(io, "+"); + } + IOStream_print(io, "\n"); + } + } + HashTable_print(table, io); +} + +/** Print an entry in a table. + * + * @param entry to print + * @param arg a pointer to an IOStream to print to + * @return 0 + */ +static int print_entry(TableArg arg, HashTable *table, HTEntry *entry){ + IOStream *io = (IOStream*)arg.ptr; + IOStream_print(io, " b=%4lx h=%08lx i=%08lx |-> e=%8p k=%8p v=%8p\n", + entry->hashcode % table->buckets_n, + entry->hashcode, + entry->index, + entry, entry->key, entry->value); + return 0; +} + +/** Print a hash table. + * + * @param table to print + */ +void HashTable_print(HashTable *table, IOStream *io){ + IOStream_print(io, "{\n"); + HashTable_map(table, print_entry, (TableArg){ ptr: io }); + IOStream_print(io, "}\n"); +} +/*==========================================================================*/ + +/** Get the next entry id to use for a table. + * + * @param table hash table + * @return non-zero entry id + */ +static inline unsigned long get_next_id(HashTable *table){ + unsigned long id; + + if(table->next_id == 0){ + table->next_id = 1; + } + id = table->next_id++; + return id; +} + +/** Add an entry to the bucket for the + * given hashcode. + * + * @param table to insert in + * @param hashcode indicates the bucket + * @param key to add an entry for + * @param value to add an entry for + * @return entry on success, 0 on failure + */ +inline HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value){ + HTEntry *entry = HTEntry_new(hashcode, key, value); + if(entry){ + entry->index = get_next_id(table); + push_on_bucket(table, hashcode, entry); + table->entry_count++; + } + return entry; +} + +/** Move the front entry for a bucket to the correct point in the bucket order as + * defined by the order function. If this is called every time a new entry is added + * the bucket will be maintained in sorted order. + * + * @param table to modify + * @param hashcode indicates the bucket + * @param order entry comparison function + * @return 0 if an entry was moved, 1 if not + */ +int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order){ + HTEntry *new_entry = NULL, *prev = NULL, *entry = NULL; + HTBucket *bucket; + int err = 1; + + bucket = get_bucket(table, hashcode); + new_entry = bucket->head; + if(!new_entry || !new_entry->next) goto exit; + for(entry = new_entry->next; entry; prev = entry, entry = entry->next){ + if(order(new_entry, entry) <= 0) break; + } + if(prev){ + err = 0; + bucket->head = new_entry->next; + new_entry->next = entry; + prev->next = new_entry; + } + exit: + return err; +} + +/** Add an entry to a hashtable. + * The entry is added to the bucket for its key's hashcode. + * + * @param table to insert in + * @param key to add an entry for + * @param value to add an entry for + * @return entry on success, 0 on failure + */ +inline HTEntry * HashTable_add(HashTable *table, void *key, void *value){ + return HashTable_add_entry(table, HashTable_key_hash(table, key), key, value); +} + + +/** Remove entries satisfying a test from the bucket for the + * given hashcode. + * + * @param table to remove from + * @param hashcode indicates the bucket + * @param test_fn test to apply to elements + * @param arg first argument to calls to test_fn + * @return number of entries removed + */ +inline int HashTable_remove_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg){ + HTBucket *bucket; + HTEntry *entry, *prev = 0, *next; + int removed_count = 0; + + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(test_fn(arg, table, entry)){ + if(prev){ + prev->next = next; + } else { + bucket->head = next; + } + bucket->count--; + table->entry_count--; + removed_count++; + HashTable_free_entry(table, entry); + entry = 0; + } + prev = entry; + } + return removed_count; +} + +/** Remove entries with a given key. + * + * @param table to remove from + * @param key of entries to remove + * @return number of entries removed + */ +inline int HashTable_remove(HashTable *table, void *key){ +#if 1 + Hashcode hashcode; + HTBucket *bucket; + HTEntry *entry, *prev = 0, *next; + int removed_count = 0; + + hashcode = HashTable_key_hash(table, key); + bucket = get_bucket(table, hashcode); + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + if(HashTable_key_equal(table, key, entry->key)){ + if(prev){ + prev->next = next; + } else { + bucket->head = next; + } + bucket->count--; + table->entry_count--; + removed_count++; + HashTable_free_entry(table, entry); + entry = 0; + } + prev = entry; + } + return removed_count; +#else + return HashTable_remove_entry(table, HashTable_key_hash(table, key), + has_key, (TableArg){ ptr: key}); +#endif +} + +/** Remove (and free) all the entries in a bucket. + * + * @param bucket to clear + */ +static inline void bucket_clear(HashTable *table, HTBucket *bucket){ + HTEntry *entry, *next; + + for(entry = bucket->head; entry; entry = next){ + next = entry->next; + HashTable_free_entry(table, entry); + } + bucket->head = 0; + table->entry_count -= bucket->count; + bucket->count = 0; +} + +/** Remove (and free) all the entries in a table. + * + * @param table to clear + */ +void HashTable_clear(HashTable *table){ + int i, n = table->buckets_n; + + for(i=0; ibuckets + i); + } +} diff --git a/tools/xfrd/hash_table.h b/tools/xfrd/hash_table.h new file mode 100644 index 0000000000..6608b49cda --- /dev/null +++ b/tools/xfrd/hash_table.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2001 - 2004 Mike Wray + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_HASH_TABLE_H_ +#define _XUTIL_HASH_TABLE_H_ + +#include "iostream.h" + +typedef unsigned long Hashcode; + +/** Type used to pass parameters to table functions. */ +typedef union TableArg { + unsigned long ul; + void *ptr; +} TableArg; + +/** An entry in a bucket list. */ +typedef struct HTEntry { + /** Hashcode of the entry's key. */ + Hashcode hashcode; + /** Identifier for this entry in the table. */ + int index; + /** The key for this entry. */ + void *key; + /** The value in this entry. */ + void *value; + /** The next entry in the list. */ + struct HTEntry *next; +} HTEntry; + +/** A bucket in a rule table. */ +typedef struct HTBucket { + /** Number of entries in the bucket. */ + int count; + /** First entry in the bucket (may be null). */ + HTEntry *head; +} HTBucket; + +/** Default number of buckets in a hash table. + * You want enough buckets so the lists in the buckets will typically be short. + * It's a good idea if this is prime, since that will help to spread hashcodes + * around the table. + */ +//#define HT_BUCKETS_N 1 +//#define HT_BUCKETS_N 3 +//#define HT_BUCKETS_N 7 +//#define HT_BUCKETS_N 17 +//#define HT_BUCKETS_N 97 +//#define HT_BUCKETS_N 211 +//#define HT_BUCKETS_N 401 +#define HT_BUCKETS_N 1021 + +typedef struct HashTable HashTable; + +/** Type for a function used to select table entries. */ +typedef int TableTestFn(TableArg arg, HashTable *table, HTEntry *entry); + +/** Type for a function to map over table entries. */ +typedef int TableMapFn(TableArg arg, HashTable *table, HTEntry *entry); + +/** Type for a function to free table entries. */ +typedef void TableFreeFn(HashTable *table, HTEntry *entry); + +/** Type for a function to hash table keys. */ +typedef Hashcode TableHashFn(void *key); + +/** Type for a function to test table keys for equality. */ +typedef int TableEqualFn(void *key1, void *key2); + +/** Type for a function to order table entries. */ +typedef int TableOrderFn(HTEntry *e1, HTEntry *e2); + +/** General hash table. + * A hash table with a list in each bucket. + * Functions can be supplied for freeing entries, hashing keys, and comparing keys. + * These all default to 0, when default behaviour treating keys as integers is used. + */ +struct HashTable { + /** Flag indicating whether the table has been initialised. */ + int init_done; + /** Next value for the id field in inserted rules. */ + unsigned long next_id; + /** Number of buckets in the bucket array. */ + int buckets_n; + /** Array of buckets, each with its own list. */ + HTBucket *buckets; + /** Number of entries in the table. */ + int entry_count; + /** Function to free keys and values in entries. */ + TableFreeFn *entry_free_fn; + /** Function to hash keys. */ + TableHashFn *key_hash_fn; + /** Function to compare keys for equality. */ + TableEqualFn *key_equal_fn; + /** Place for the user of the table to hang extra data. */ + void *user_data; +}; + +extern HashTable *HashTable_new(int bucket_n); +extern void HashTable_free(HashTable *table); +extern HTEntry * HTEntry_new(Hashcode hashcode, void *key, void *value); +extern void HTEntry_free(HTEntry *entry); +extern int HashTable_set_bucket_n(HashTable *table, int bucket_n); +extern void HashTable_clear(HashTable *table); +extern HTEntry * HashTable_add_entry(HashTable *table, Hashcode hashcode, void *key, void *value); +extern HTEntry * HashTable_get_entry(HashTable *table, void *key); +extern HTEntry * HashTable_add(HashTable *table, void *key, void *value); +extern void * HashTable_get(HashTable *table, void *key); +extern int HashTable_remove(HashTable *table, void *key); +extern HTEntry * HashTable_find_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg); +extern int HashTable_remove_entry(HashTable *table, Hashcode hashcode, + TableTestFn *test_fn, TableArg arg); +//extern int HashTable_map(HashTable *table, TableMapFn *map_fn, TableArg arg); +extern void HashTable_print(HashTable *table, IOStream *out); +extern int HashTable_set_buckets_n(HashTable *table, int buckets_n); +extern int HashTable_adjust(HashTable *table, int buckets_min); +extern void pseudo_des(unsigned long *pleft, unsigned long *pright); +extern Hashcode hash_string(char *s); + +extern int HashTable_order_bucket(HashTable *table, Hashcode hashcode, TableOrderFn *order); + +/** Control whether to use hashing based on DES or simple + * hashing. DES hashing is `more random' but much more expensive. + */ +#define HASH_PSEUDO_DES 0 + +/** Hash a long using a quick and dirty linear congruential random number generator. + * See `Numerical Recipes in C', Chapter 7, "An Even Quicker Generator". + * + * @param a value to hash + * @return hashed input + */ +static inline unsigned long lcrng_hash(unsigned long a){ + return (1664525L * a + 1013904223L); +} + +/** Hash an unsigned long. + * + * @param a input to hash + * @return hashcode + */ +static inline Hashcode hash_ul(unsigned long a){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = 0L; + pseudo_des(&left, &right); + return right; +#else + a = lcrng_hash(a); + a = lcrng_hash(a); + return a; +#endif +} + +/** Hash two unsigned longs together. + * + * @param a input to hash + * @param b input to hash + * @return hashcode + */ +static inline Hashcode hash_2ul(unsigned long a, unsigned long b){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = b; + pseudo_des(&left, &right); + return right; +#else + a = lcrng_hash(a); + a ^= b; + a = lcrng_hash(a); + return a; +#endif +} + +/** Hash a hashcode and an unsigned long together. + * + * @param a input hashcode + * @param b input to hash + * @return hashcode + */ +static inline Hashcode hash_hul(Hashcode a, unsigned long b){ +#if HASH_PSEUDO_DES + unsigned long left = a; + unsigned long right = b; + pseudo_des(&left, &right); + return right; +#else + a ^= b; + a = lcrng_hash(a); + return a; +#endif +} + +/** Macro to declare variables for HashTable_for_each() to use. + * + * @param entry variable that is set to entries in the table + */ +#define HashTable_for_decl(entry) \ + HashTable *_var_table; \ + HTBucket *_var_bucket; \ + HTBucket *_var_end; \ + HTEntry *_var_next; \ + HTEntry *entry + +/** Macro to iterate over the entries in a hashtable. + * Must be in a scope where HashTable_for_decl() has been used to declare + * variables for it to use. + * The variable 'entry' is iterated over entries in the table. + * The code produced is syntactically a loop, so it must be followed by + * a loop body, typically some statements in braces: + * HashTable_for_each(entry, table){ ...loop body... } + * + * HashTable_for_each() and HashTable_for_decl() cannot be used for nested + * loops as variables will clash. + * + * @note The simplest way to code a direct loop over the entries in a hashtable + * is to use a loop over the buckets, with a nested loop over the entries + * in a bucket. Using this approach in a macro means the macro contains + * an opening brace, and calls to it must be followed by 2 braces! + * To avoid this the code has been restructured so that it is a for loop. + * So that statements could be used in the test expression of the for loop, + * we have used the gcc statement expression extension ({ ... }). + * + * @param entry variable to iterate over the entries + * @param table to iterate over (non-null) + */ +#define HashTable_for_each(entry, table) \ + _var_table = table; \ + _var_bucket = _var_table->buckets; \ + _var_end = _var_bucket + _var_table->buckets_n; \ + for(entry=0, _var_next=0; \ + ({ if(_var_next){ \ + entry = _var_next; \ + _var_next = entry->next; \ + } else { \ + while(_var_bucket < _var_end){ \ + entry = _var_bucket->head; \ + _var_bucket++; \ + if(entry){ \ + _var_next = entry->next; \ + break; \ + } \ + } \ + }; \ + entry; }); \ + entry = _var_next ) + +/** Map a function over the entries in a table. + * Mapping stops when the function returns a non-zero value. + * Uses the gcc statement expression extension ({ ... }). + * + * @param table to map over + * @param fn function to apply to entries + * @param arg first argument to call the function with + * @return 0 if fn always returned 0, first non-zero value otherwise + */ +#define HashTable_map(table, fn, arg) \ + ({ HashTable_for_decl(_var_entry); \ + TableArg _var_arg = arg; \ + int _var_value = 0; \ + HashTable_for_each(_var_entry, table){ \ + if((_var_value = fn(_var_arg, _var_table, _var_entry))) break; \ + } \ + _var_value; }) + +/** Cast x to the type for a key or value in a hash table. + * This avoids compiler warnings when using short integers + * as keys or values (especially on 64-bit platforms). + */ +#define HKEY(x) ((void*)(unsigned long)(x)) + +/** Cast x from the type for a key or value in a hash table. + * to an unsigned long. This avoids compiler warnings when using + * short integers as keys or values (especially on 64-bit platforms). + */ +#define HVAL(x) ((unsigned long)(x)) + +#endif /* !_XUTIL_HASH_TABLE_H_ */ diff --git a/tools/xfrd/lexis.c b/tools/xfrd/lexis.c new file mode 100644 index 0000000000..26d2ec4d5b --- /dev/null +++ b/tools/xfrd/lexis.c @@ -0,0 +1,93 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * Lexical analysis. + */ + +#include "sys_string.h" +#include "lexis.h" +#include + +/** Check if a value lies in a (closed) range. + * + * @param x value to test + * @param lo low end of the range + * @param hi high end of the range + * @return 1 if x is in the interval [lo, hi], 0 otherwise + */ +inline static int in_range(int x, int lo, int hi){ + return (lo <= x) && (x <= hi); +} + +/** Determine if a string is an (unsigned) decimal number. + * + * @param s pointer to characters to test + * @param n length of string + * @return 1 if s is a decimal number, 0 otherwise. + */ +int is_decimal_number(const char *s, int n){ + int i; + if(n <= 0)return 0; + for(i = 0; i < n; i++){ + if(!in_decimal_digit_class(s[i])) return 0; + } + return 1; +} + +/** Determine if a string is a hex number. + * Hex numbers are 0, or start with 0x or 0X followed + * by a non-zero number of hex digits (0-9,a-f,A-F). + * + * @param s pointer to characters to test + * @param n length of string + * @return 1 if s is a hex number, 0 otherwise. + */ +int is_hex_number(const char *s, int n){ + int i; + if(n <= 0) return 0; + if(n == 1){ + return s[0]=='0'; + } + if(n <= 3) return 0; + if(s[0] != '0' || (s[1] != 'x' && s[1] != 'X')) return 0; + for(i = 2; i < n; i++){ + if(!in_hex_digit_class(s[i])) return 0; + } + return 1; +} + +/** Test if a string matches a keyword. + * The comparison is case-insensitive. + * The comparison fails if either argument is null. + * + * @param s string + * @param k keyword + * @return 1 if they match, 0 otherwise + */ +int is_keyword(const char *s, const char *k){ + return s && k && !strcasecmp(s, k); +} + +/** Test if a string matches a character. + * + * @param s string + * @param c character (non-null) + * @return 1 if s contains exactly c, 0 otherwise + */ +int is_keychar(const char *s, char c){ + return c && (s[0] == c) && !s[1]; +} diff --git a/tools/xfrd/lexis.h b/tools/xfrd/lexis.h new file mode 100644 index 0000000000..796a8a415a --- /dev/null +++ b/tools/xfrd/lexis.h @@ -0,0 +1,127 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_LEXIS_H_ +#define _XUTIL_LEXIS_H_ + +#include "sys_string.h" + +#ifdef __KERNEL__ +# include +#else +# include +#endif + +/** @file + * Lexical analysis. + */ + +/** Class of characters treated as space. */ +#define space_class ((char []){ '\n', '\r', '\t', ' ', '\f' , 0 }) + +/** Class of separator characters. */ +#define sep_class "{}()<>[]@!;" + +#define comment_class "#" + +/** Determine if a character is in a given class. + * + * @param c character to test + * @param s null-terminated string of characters in the class + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_class(int c, const char *s){ + return s && (strchr(s, c) != 0); +} + +/** Determine if a character is in the space class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_space_class(int c){ + return in_class(c, space_class); +} + +static inline int in_comment_class(int c){ + return in_class(c, comment_class); +} + +/** Determine if a character is in the separator class. + * Separator characters terminate tokens, and do not need space + * to separate them. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_sep_class(int c){ + return in_class(c, sep_class); +} + +/** Determine if a character is in the alpha class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_alpha_class(int c){ + return isalpha(c); +} + +/** Determine if a character is in the octal digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_octal_digit_class(int c){ + return '0' <= c && c <= '7'; +} + +/** Determine if a character is in the decimal digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_decimal_digit_class(int c){ + return isdigit(c); +} + +/** Determine if a character is in the hex digit class. + * + * @param c character to test + * @return 1 if c is in the class, 0 otherwise. + */ +static inline int in_hex_digit_class(int c){ + return isdigit(c) || in_class(c, "abcdefABCDEF"); +} + + +static inline int in_string_quote_class(int c){ + return in_class(c, "'\""); +} + +static inline int in_printable_class(int c){ + return ('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || ('0' <= c && c <= '9') + || in_class(c, "!$%&*+,-./:;<=>?@^_`{|}~"); +} + +extern int is_decimal_number(const char *s, int n); +extern int is_hex_number(const char *s, int n); +extern int is_keyword(const char *s, const char *k); +extern int is_keychar(const char *s, char c); + +#endif /* !_XUTIL_LEXIS_H_ */ diff --git a/tools/xfrd/lzi_stream.c b/tools/xfrd/lzi_stream.c new file mode 100644 index 0000000000..5fbec775c4 --- /dev/null +++ b/tools/xfrd/lzi_stream.c @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/** @file + * An IOStream implementation using LZI to provide compression and decompression. + * This is designed to provide compression without output latency. + * Flushing an LZI stream flushes all pending data to the underlying stream. + * This is essential for stream-based (e.g. networked) applications. + * + * A compressed data stream is a sequence of blocks. + * Each block is the block size followed by the compressed data. + * The last block has size zero. + * Sizes are 4-byte unsigned in network order. + * + * This format allows compressed data to be read from a stream without reading + * past the logical end of compressed data. + * + * @author Mike Wray + */ +#ifndef __KERNEL__ + +#include +#include +#include +#include + +#include "zlib.h" + +#include "allocate.h" +#include "lzi_stream.h" +#include "file_stream.h" +#include "marshal.h" + +#define dprintf(fmt, args...) fprintf(stdout, "[DEBUG] LZI>%s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) fprintf(stderr, "[WARN] LZI>%s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) fprintf(stdout, "[INFO] LZI>%s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) fprintf(stderr, "[ERROR] LZI>%s" fmt, __FUNCTION__, ##args) + +static int lzi_read(IOStream *s, void *buf, size_t n); +static int lzi_write(IOStream *s, const void *buf, size_t n); +static int lzi_error(IOStream *s); +static int lzi_close(IOStream *s); +static void lzi_free(IOStream *s); +static int lzi_flush(IOStream *s); + +enum { + LZI_WRITE = 1, + LZI_READ = 2, +}; + +/** Methods used by a gzFile* IOStream. */ +static const IOMethods lzi_methods = { + read: lzi_read, + write: lzi_write, + error: lzi_error, + close: lzi_close, + free: lzi_free, + flush: lzi_flush, +}; + +#define BUFFER_SIZE (512 * 1024) + +typedef struct LZIState { + z_stream zstream; + void *inbuf; + uint32_t inbuf_size; + void *outbuf; + uint32_t outbuf_size; + /** Underlying stream for I/O. */ + IOStream *io; + /** Flags. */ + int flags; + /** Error indicator. */ + int error; + int eof; + int plain_bytes; + int comp_bytes; + int zstream_initialized; + int flushed; +} LZIState; + +static inline int LZIState_writeable(LZIState *s){ + return (s->flags & LZI_WRITE) != 0; +} + +static inline int LZIState_readable(LZIState *s){ + return (s->flags & LZI_READ) != 0; +} + +void LZIState_free(LZIState *z){ + if(!z) return; + if(z->zstream_initialized){ + if(LZIState_writeable(z)){ + deflateEnd(&z->zstream); + } else if(LZIState_readable(z)){ + inflateEnd(&z->zstream); + } + } + deallocate(z->inbuf); + deallocate(z->outbuf); + deallocate(z); +} + +static int mode_flags(const char *mode, int *flags){ + int err = 0; + int r=0, w=0; + if(!mode){ + err = -EINVAL; + goto exit; + } + for(; *mode; mode++){ + if(*mode == 'w') w = 1; + if(*mode == 'r') r = 1; + } + if(r + w != 1){ + err = -EINVAL; + goto exit; + } + if(r) *flags |= LZI_READ; + if(w) *flags |= LZI_WRITE; + exit: + return err; +} + +/** Get the stream state. + * + * @param s lzi stream + * @return stream state. + */ +static inline LZIState * lzi_state(IOStream *io){ + return (LZIState*)io->data; +} + +IOStream *lzi_stream_io(IOStream *io){ + LZIState *s = lzi_state(io); + return s->io; +} + +static inline void set_error(LZIState *s, int err){ + if(err < 0 && !s->error){ + s->error = err; + } +} + +static int zerror(LZIState *s, int err){ + if(err){ + //dprintf("> err=%d\n", err); + if(err < 0) set_error(s, -EIO); + } + return s->error; +} + +int lzi_stream_plain_bytes(IOStream *io){ + LZIState *s = lzi_state(io); + return s->plain_bytes; +} + +int lzi_stream_comp_bytes(IOStream *io){ + LZIState *s = lzi_state(io); + return s->comp_bytes; +} + +float lzi_stream_ratio(IOStream *io){ + LZIState *s = lzi_state(io); + float ratio = 0.0; + if(s->comp_bytes){ + ratio = ((float) s->comp_bytes)/((float) s->plain_bytes); + } + return ratio; +} + +static int alloc(void **p, int n){ + *p = allocate(n); + return (p ? 0 : -ENOMEM); +} + +LZIState * LZIState_new(IOStream *io, int flags){ + int err = -ENOMEM; + int zlevel = Z_BEST_SPEED; // Level 1 compression - fastest. + int zstrategy = Z_DEFAULT_STRATEGY; + int zwindow = MAX_WBITS; + int zmemory = 8; + LZIState *z = ALLOCATE(LZIState); + + //dprintf(">\n"); + if(!z) goto exit; + z->io = io; + z->flags = flags; + + if(LZIState_writeable(z)){ + z->outbuf_size = BUFFER_SIZE; + /* windowBits is passed < 0 to suppress zlib header */ + err = deflateInit2(&z->zstream, zlevel, Z_DEFLATED, -zwindow, zmemory, zstrategy); + if (err != Z_OK) goto exit; + z->zstream_initialized = 1; + err = alloc(&z->outbuf, z->outbuf_size); + if(err) goto exit; + z->zstream.next_out = z->outbuf; + z->zstream.avail_out = z->outbuf_size; + } else { + z->inbuf_size = BUFFER_SIZE; + err = alloc(&z->inbuf, z->inbuf_size); + if(err) goto exit; + ///z->zstream.next_in = z->inbuf; + + /* windowBits is passed < 0 to tell that there is no zlib header. + * Note that in this case inflate *requires* an extra "dummy" byte + * after the compressed stream in order to complete decompression and + * return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes are + * present after the compressed stream. + */ + err = inflateInit2(&z->zstream, -zwindow); + if(err != Z_OK) goto exit; + z->zstream_initialized = 1; + } + + exit: + if(err){ + LZIState_free(z); + z = NULL; + } + //dprintf("< z=%p\n", z); + return z; +} + +int read_block(LZIState *s){ + int err = 0, k = 0; + //dprintf(">\n"); + if(s->eof) goto exit; + err = unmarshal_uint32(s->io, &k); + if(err) goto exit; + if(k > s->inbuf_size){ + err = -EINVAL; + goto exit; + } + if(k){ + err = unmarshal_bytes(s->io, s->inbuf, k); + if(err) goto exit; + } else { + s->eof = 1; + } + s->zstream.avail_in = k; + s->zstream.next_in = s->inbuf; + s->comp_bytes += 4; + s->comp_bytes += k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int write_block(LZIState *s){ + int err = 0; + int k = ((char*)s->zstream.next_out) - ((char*)s->outbuf); + //int k2 = s->outbuf_size - s->zstream.avail_out; + //dprintf("> k=%d k2=%d\n", k, k2); + if(!k) goto exit; + err = marshal_uint32(s->io, k); + if(err) goto exit; + err = marshal_bytes(s->io, s->outbuf, k); + if(err) goto exit; + s->zstream.next_out = s->outbuf; + s->zstream.avail_out = s->outbuf_size; + s->comp_bytes += 4; + s->comp_bytes += k; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int write_terminator(LZIState *s){ + int err = 0; + char c = 0; + err = marshal_uint32(s->io, 1); + if(err) goto exit; + err = marshal_bytes(s->io, &c, 1); + if(err) goto exit; + err = marshal_uint32(s->io, 0); + if(err) goto exit; + s->comp_bytes += 9; + exit: + return err; +} + +/** Write to the underlying stream using fwrite(); + * + * @param io destination + * @param buf data + * @param n number of bytes to write + * @return number of bytes written + */ +static int lzi_write(IOStream *io, const void *buf, size_t n){ + int err = 0; + LZIState *s = lzi_state(io); + + //dprintf("> buf=%p n=%d\n", buf, n); + if(!LZIState_writeable(s)){ + err = -EINVAL; + goto exit; + } + s->flushed = 0; + s->zstream.next_in = (void*)buf; + s->zstream.avail_in = n; + while(s->zstream.avail_in){ + if(s->zstream.avail_out == 0){ + err = write_block(s); + if(err) goto exit; + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> 1 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out); + err = zerror(s, deflate(&s->zstream, Z_NO_FLUSH)); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> 2 deflate next_in=%p next_out=%p\n", s->zstream.next_in, s->zstream.next_out); + if(err) goto exit; + } + err = n; + s->plain_bytes += n; + exit: + //dprintf("< err=%d\n", err); + return err; +} + + +/** Read from the underlying stream. + * + * @param io input + * @param buf where to put input + * @param n number of bytes to read + * @return number of bytes read + */ +static int lzi_read(IOStream *io, void *buf, size_t n){ + int err, zerr; + LZIState *s = lzi_state(io); + + //dprintf("> n=%d\n", n); + if(!LZIState_readable(s)){ + err = -EINVAL; + goto exit; + } + s->zstream.next_out = buf; + s->zstream.avail_out = n; + while(s->zstream.avail_out){ + if(s->zstream.avail_in == 0){ + err = read_block(s); + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + zerr = inflate(&s->zstream, Z_NO_FLUSH); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + if(zerr == Z_STREAM_END) break; + //dprintf("> zerr=%d\n", zerr); + err = zerror(s, zerr); + if(err) goto exit; + } + err = n - s->zstream.avail_out; + s->plain_bytes += err; + exit: + set_error(s, err); + //dprintf("< err=%d\n", err); + return err; +} + +static int flush_output(LZIState *s, int mode){ + int err = 0, zerr; + int done = 0; + int avail_out_old; + + //dprintf("> avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + if(s->flushed == 1 + mode) goto exit; + //s->zstream.avail_in = 0; /* should be zero already anyway */ + for(;;){ + // Write any available output. + if(done || s->zstream.avail_out == 0){ + err = write_block(s); + if(err) goto exit; + if(done) break; + } + //dprintf("> 1 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + avail_out_old = s->zstream.avail_out; + zerr = deflate(&s->zstream, mode); + err = zerror(s, zerr); + //dprintf("> 2 deflate avail_in=%d avail_out=%d\n", s->zstream.avail_in, s->zstream.avail_out); + //dprintf("> deflate=%d\n", err); + //done = (s->zstream.avail_out != 0); + //done = (s->zstream.avail_in == 0) && (s->zstream.avail_out == avail_out_old); + if(0 && mode == Z_FINISH){ + done = (zerr == Z_STREAM_END); + } else { + done = (s->zstream.avail_in == 0) + //&& (s->zstream.avail_out == avail_out_old) + && (s->zstream.avail_out != 0); + } + } + s->flushed = 1 + mode; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +/** Flush any pending input to the underlying stream. + * + * @param s lzi stream + * @return 0 on success, error code otherwise + */ +static int lzi_flush(IOStream *io){ + int err = 0; + LZIState *s = lzi_state(io); + //dprintf(">\n"); + if(!LZIState_writeable(s)){ + err = -EINVAL; + goto exit; + } + err = flush_output(s, Z_SYNC_FLUSH); + if(err) goto exit; + err = IOStream_flush(s->io); + exit: + set_error(s, err); + //dprintf("< err=%d\n", err); + return (err < 0 ? err : 0); +} + +/** Check if a stream has an error. + * + * @param s lzi stream + * @return code if has an error, 0 otherwise + */ +static int lzi_error(IOStream *s){ + int err = 0; + LZIState *state = lzi_state(s); + err = state->error; + if(err) goto exit; + err = IOStream_error(state->io); + exit: + return err; +} + +/** Close an lzi stream. + * + * @param s lzi stream to close + * @return result of the close + */ +static int lzi_close(IOStream *io){ + int err = 0; + LZIState *s = lzi_state(io); + if(LZIState_writeable(s)){ + err = flush_output(s, Z_FINISH); + if(err) goto exit; + err = write_terminator(s); + if(err) goto exit; + err = IOStream_flush(s->io); + } + exit: + err = IOStream_close(s->io); + set_error(s, err); + return err; +} + +/** Free an lzi stream. + * + * @param s lzi stream + */ +static void lzi_free(IOStream *s){ + LZIState *state = lzi_state(s); + IOStream_free(state->io); + LZIState_free(state); + s->data = NULL; +} + +/** Create an lzi stream for an IOStream. + * + * @param io stream to wrap + * @return new IOStream using f for i/o + */ +IOStream *lzi_stream_new(IOStream *io, const char *mode){ + int err = -ENOMEM; + int flags = 0; + IOStream *zio = NULL; + LZIState *state = NULL; + + zio = ALLOCATE(IOStream); + if(!zio) goto exit; + err = mode_flags(mode, &flags); + if(err) goto exit; + state = LZIState_new(io, flags); + if(!state) goto exit; + err = 0; + zio->data = state; + zio->methods = &lzi_methods; + exit: + if(err){ + if(state) LZIState_free(state); + if(zio) deallocate(zio); + zio = NULL; + } + return zio; +} + +/** IOStream version of fdopen(). + * + * @param fd file descriptor + * @param flags giving the mode to open in (as for fdopen()) + * @return new stream for the open file, or NULL if failed + */ +IOStream *lzi_stream_fdopen(int fd, const char *mode){ + int err = -ENOMEM; + IOStream *io = NULL, *zio = NULL; + io = file_stream_fdopen(fd, mode); + if(!io) goto exit; + zio = lzi_stream_new(io, mode); + if(!io) goto exit; + err = 0; + exit: + if(err){ + IOStream_free(io); + IOStream_free(zio); + zio = NULL; + } + return zio; +} +#endif diff --git a/tools/xfrd/lzi_stream.h b/tools/xfrd/lzi_stream.h new file mode 100644 index 0000000000..959059eccf --- /dev/null +++ b/tools/xfrd/lzi_stream.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2003 Hewlett-Packard Company. + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _XUTIL_LZI_STREAM_H_ +#define _XUTIL_LZI_STREAM_H_ + +#ifndef __KERNEL__ +#include "iostream.h" + +extern IOStream *lzi_stream_new(IOStream *io, const char *mode); +extern IOStream *lzi_stream_fopen(const char *file, const char *mode); +extern IOStream *lzi_stream_fdopen(int fd, const char *mode); +extern IOStream *lzi_stream_io(IOStream *zio); + +extern int lzi_stream_plain_bytes(IOStream *io); +extern int lzi_stream_comp_bytes(IOStream *io); +extern float lzi_stream_ratio(IOStream *io); + +#endif +#endif /* !_XUTIL_LZI_STREAM_H_ */ diff --git a/tools/xfrd/marshal.c b/tools/xfrd/marshal.c new file mode 100644 index 0000000000..21691d4412 --- /dev/null +++ b/tools/xfrd/marshal.c @@ -0,0 +1,207 @@ +#include +#include "sys_net.h" +#include "allocate.h" +#include "marshal.h" + +#define dprintf(fmt, args...) IOStream_print(iostdout, "[DEBUG] %s" fmt, __FUNCTION__, ##args) +#define wprintf(fmt, args...) IOStream_print(iostderr, "[WARN] %s" fmt, __FUNCTION__, ##args) +#define iprintf(fmt, args...) IOStream_print(iostdout, "[INFO] %s" fmt, __FUNCTION__, ##args) +#define eprintf(fmt, args...) IOStream_print(iostderr, "[ERROR] %s" fmt, __FUNCTION__, ##args) + + +#define ARRAY_SIZE(ary) (sizeof(ary)/sizeof((ary)[0])) + +/* Messages are coded as msgid followed by message fields. + * Initial message on any channel is hello - so can check version + * compatibility. + * + * char* -> uint16_t:n + * ints/uints go as suitable number of bytes (e.g. uint16_t is 2 bytes). + * optional fields go as '1' or '0' (the 0/1 is 1 byte). + * lists go as ('1' )* '0' + */ + +int marshal_flush(IOStream *io){ + int err = 0; + err = IOStream_flush(io); + return err; +} + +int marshal_bytes(IOStream *io, void *s, uint32_t s_n){ + int err = 0; + int n; + n = IOStream_write(io, s, s_n); + if(n < 0){ + err = n; + } else if (n < s_n){ + wprintf("> Wanted %d, got %d\n", s_n, n); + err = -EIO; + } + return err; +} + +int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n){ + int err = 0; + int n; + //dprintf("> s_n=%d\n", s_n); + n = IOStream_read(io, s, s_n); + //dprintf("> n=%d\n", n); + if(n < 0){ + err = n; + } else if(n < s_n){ + wprintf("> Wanted %d, got %d\n", s_n, n); + err = -EIO; + } + //dprintf("< err=%d\n", err); + return err; +} + +int marshal_uint8(IOStream *io, uint8_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_uint8(IOStream *io, uint8_t *x){ + return unmarshal_bytes(io, x, sizeof(*x)); +} + +int marshal_uint16(IOStream *io, uint16_t x){ + x = htons(x); + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_uint16(IOStream *io, uint16_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohs(*x); + return err; +} + +int marshal_int32(IOStream *io, int32_t x){ + int err = 0; + //dprintf("> x=%d\n", x); + x = htonl(x); + err = marshal_bytes(io, &x, sizeof(x)); + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_int32(IOStream *io, int32_t *x){ + int err = 0; + //dprintf(">\n"); + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohl(*x); + //dprintf("< err=%d x=%d\n", err, *x); + return err; +} + +int marshal_uint32(IOStream *io, uint32_t x){ + int err = 0; + //dprintf("> x=%u\n", x); + x = htonl(x); + err = marshal_bytes(io, &x, sizeof(x)); + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_uint32(IOStream *io, uint32_t *x){ + int err = 0; + //dprintf(">\n"); + err = unmarshal_bytes(io, x, sizeof(*x)); + *x = ntohl(*x); + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int marshal_uint64(IOStream *io, uint64_t x){ + int err; + err = marshal_uint32(io, (uint32_t) ((x >> 32) & 0xffffffff)); + if(err) goto exit; + err = marshal_uint32(io, (uint32_t) ( x & 0xffffffff)); + exit: + return err; +} + +int unmarshal_uint64(IOStream *io, uint64_t *x){ + int err = 0; + uint32_t hi, lo; + err = unmarshal_uint32(io, &hi); + if(err) goto exit; + err = unmarshal_uint32(io, &lo); + *x = (((uint64_t) hi) << 32) | lo; + exit: + return err; +} + +int marshal_net16(IOStream *io, net16_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_net16(IOStream *io, net16_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + return err; +} + +int marshal_net32(IOStream *io, net32_t x){ + return marshal_bytes(io, &x, sizeof(x)); +} + +int unmarshal_net32(IOStream *io, net32_t *x){ + int err = 0; + err = unmarshal_bytes(io, x, sizeof(*x)); + return err; +} + +int marshal_string(IOStream *io, char *s, uint32_t s_n){ + int err; + //dprintf("> s=%s\n", s); + err = marshal_uint32(io, s_n); + if(err) goto exit; + err = marshal_bytes(io, s, s_n); + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unmarshal_string(IOStream *io, char *s, uint32_t s_n){ + int err = 0, val_n = 0; + //dprintf(">\n"); + err = unmarshal_uint32(io, &val_n); + if(err) goto exit; + if(val_n >= s_n){ + err = -EINVAL; + goto exit; + } + err = unmarshal_bytes(io, s, val_n); + if(err) goto exit; + s[val_n] = '\0'; + exit: + //dprintf("< err=%d s=%s\n", err, s); + return err; +} + +int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n){ + int err = 0, val_n = 0; + char *val = NULL; + //dprintf(">\n"); + err = unmarshal_uint32(io, &val_n); + if(err) goto exit; + val = allocate(val_n + 1); + if(!val){ + err = -ENOMEM; + goto exit; + } + err = unmarshal_bytes(io, val, val_n); + if(err) goto exit; + val[val_n] = '\0'; + exit: + if(err){ + if(val) deallocate(val); + val = NULL; + val_n = 0; + } + *s = val; + if(s_n) *s_n = val_n; + //dprintf("< err=%d s=%s\n", err, *s); + return err; +} diff --git a/tools/xfrd/marshal.h b/tools/xfrd/marshal.h new file mode 100644 index 0000000000..65e9682cea --- /dev/null +++ b/tools/xfrd/marshal.h @@ -0,0 +1,42 @@ +#ifndef _XUTIL_MARSHAL_H_ +#define _XUTIL_MARSHAL_H_ + +#include "iostream.h" + +/** A 16-bit uint in network order, e.g. a port number. */ +typedef uint16_t net16_t; + +/** A 32-bit uint in network order, e.g. an IP address. */ +typedef uint32_t net32_t; + +extern int marshal_flush(IOStream *io); + +extern int marshal_bytes(IOStream *io, void *s, uint32_t s_n); +extern int unmarshal_bytes(IOStream *io, void *s, uint32_t s_n); + +extern int marshal_uint8(IOStream *io, uint8_t x); +extern int unmarshal_uint8(IOStream *io, uint8_t *x); + +extern int marshal_uint16(IOStream *io, uint16_t x); +extern int unmarshal_uint16(IOStream *io, uint16_t *x); + +extern int marshal_uint32(IOStream *io, uint32_t x); +extern int unmarshal_uint32(IOStream *io, uint32_t *x); + +extern int marshal_int32(IOStream *io, int32_t x); +extern int unmarshal_int32(IOStream *io, int32_t *x); + +extern int marshal_uint64(IOStream *io, uint64_t x); +extern int unmarshal_uint64(IOStream *io, uint64_t *x); + +extern int marshal_net16(IOStream *io, net16_t x); +extern int unmarshal_net16(IOStream *io, net16_t *x); + +extern int marshal_net32(IOStream *io, net32_t x); +extern int unmarshal_net32(IOStream *io, net32_t *x); + +extern int marshal_string(IOStream *io, char *s, uint32_t s_n); +extern int unmarshal_string(IOStream *io, char *s, uint32_t s_n); +extern int unmarshal_new_string(IOStream *io, char **s, uint32_t *s_n); + +#endif /* ! _XUTIL_MARSHAL_H_ */ diff --git a/tools/xfrd/select.c b/tools/xfrd/select.c new file mode 100644 index 0000000000..bdaccfe000 --- /dev/null +++ b/tools/xfrd/select.c @@ -0,0 +1,50 @@ +#include +#include +#include + +#include "select.h" + +/** Zero all the file descriptor sets. + * + * @param set select set + * @param fd file descriptor + * @return 0 on success, -1 otherwise + */ +void SelectSet_zero(SelectSet *set){ + set->n = 0; + FD_ZERO(&set->rd); + FD_ZERO(&set->wr); + FD_ZERO(&set->er); +} + +/** Add a file descriptor to the write set. + * + * @param set select set + * @param fd file descriptor + * @return 0 on success, -1 otherwise + */ +void SelectSet_add_read(SelectSet *set, int fd){ + FD_SET(fd, &set->rd); + if(fd > set->n) set->n = fd; +} + +/** Add a file descriptor to the write set. + * + * @param set select set + * @param fd file descriptor + * @return 0 on success, -1 otherwise + */ +void SelectSet_add_write(SelectSet *set, int fd){ + FD_SET(fd, &set->wr); + if(fd > set->n) set->n = fd; +} + +/** Select on file descriptors. + * + * @param set select set + * @param timeout timeout (may be NULL for no timeout) + * @return 0 on success, -1 otherwise + */ +int SelectSet_select(SelectSet *set, struct timeval *timeout){ + return select(set->n+1, &set->rd, &set->wr, &set->er, timeout); +} diff --git a/tools/xfrd/select.h b/tools/xfrd/select.h new file mode 100644 index 0000000000..2453f98536 --- /dev/null +++ b/tools/xfrd/select.h @@ -0,0 +1,16 @@ +#ifndef _XFRD_SELECT_H_ +#define _XFRD_SELECT_H_ + +/** Set of file descriptors for select. + */ +typedef struct SelectSet { + int n; + fd_set rd, wr, er; +} SelectSet; + +extern void SelectSet_zero(SelectSet *set); +extern void SelectSet_add_read(SelectSet *set, int fd); +extern void SelectSet_add_write(SelectSet *set, int fd); +extern int SelectSet_select(SelectSet *set, struct timeval *timeout); + +#endif /* ! _XFRD_SELECT_H_ */ diff --git a/tools/xfrd/sxpr.c b/tools/xfrd/sxpr.c new file mode 100644 index 0000000000..81fd14801c --- /dev/null +++ b/tools/xfrd/sxpr.c @@ -0,0 +1,950 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "sys_string.h" +#include "lexis.h" +#include "sys_net.h" +#include "hash_table.h" +#include "sxpr.h" + +#include +#undef free + +/** @file + * General representation of sxprs. + * Includes print, equal, and free functions for the sxpr types. + * + * Zero memory containing an Sxpr will have the value ONONE - this is intentional. + * When a function returning an sxpr cannot allocate memory we return ONOMEM. + * + */ + +static int atom_print(IOStream *io, Sxpr obj, unsigned flags); +static int atom_equal(Sxpr x, Sxpr y); +static void atom_free(Sxpr obj); + +static int string_print(IOStream *io, Sxpr obj, unsigned flags); +static int string_equal(Sxpr x, Sxpr y); +static void string_free(Sxpr obj); + +static int cons_print(IOStream *io, Sxpr obj, unsigned flags); +static int cons_equal(Sxpr x, Sxpr y); +static void cons_free(Sxpr obj); + +static int null_print(IOStream *io, Sxpr obj, unsigned flags); +static int none_print(IOStream *io, Sxpr obj, unsigned flags); +static int int_print(IOStream *io, Sxpr obj, unsigned flags); +static int bool_print(IOStream *io, Sxpr obj, unsigned flags); + +/** Type definitions. */ +static SxprType types[1024] = { + [T_NONE] { type: T_NONE, name: "none", print: none_print }, + [T_NULL] { type: T_NULL, name: "null", print: null_print }, + [T_UINT] { type: T_UINT, name: "int", print: int_print, }, + [T_BOOL] { type: T_BOOL, name: "bool", print: bool_print, }, + [T_ATOM] { type: T_ATOM, name: "atom", print: atom_print, + pointer: TRUE, + free: atom_free, + equal: atom_equal, + }, + [T_STRING] { type: T_STRING, name: "string", print: string_print, + pointer: TRUE, + free: string_free, + equal: string_equal, + }, + [T_CONS] { type: T_CONS, name: "cons", print: cons_print, + pointer: TRUE, + free: cons_free, + equal: cons_equal, + }, +}; + +/** Number of entries in the types array. */ +static int type_sup = sizeof(types)/sizeof(types[0]); + +/** Get the type definition for a given type code. + * + * @param ty type code + * @return type definition or null + */ +SxprType *get_sxpr_type(int ty){ + if(0 <= ty && ty < type_sup){ + return types+ty; + } + return NULL; +} + +/** The default print function. + * + * @param io stream to print to + * @param x sxpr to print + * @param flags print flags + * @return number of bytes written on success + */ +int default_print(IOStream *io, Sxpr x, unsigned flags){ + return IOStream_print(io, "#<%u %lu>\n", get_type(x), get_ul(x)); +} + +/** The default equal function. + * Uses eq(). + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int default_equal(Sxpr x, Sxpr y){ + return eq(x, y); +} + +/** General sxpr print function. + * Prints an sxpr on a stream using the print function for the sxpr type. + * Printing is controlled by flags from the PrintFlags enum. + * If PRINT_TYPE is in the flags the sxpr type is printed before the sxpr + * (for debugging). + * + * @param io stream to print to + * @param x sxpr to print + * @param flags print flags + * @return number of bytes written + */ +int objprint(IOStream *io, Sxpr x, unsigned flags){ + SxprType *def = get_sxpr_type(get_type(x)); + ObjPrintFn *print_fn = (def && def->print ? def->print : default_print); + int k = 0; + if(!io) return k; + if(flags & PRINT_TYPE){ + k += IOStream_print(io, "%s:", def->name); + } + k += print_fn(io, x, flags); + return k; +} + +/** General sxpr free function. + * Frees an sxpr using the free function for its type. + * Free functions must recursively free any subsxprs. + * If no function is defined then the default is to + * free sxprs whose type has pointer true. + * Sxprs must not be used after freeing. + * + * @param x sxpr to free + */ +void objfree(Sxpr x){ + SxprType *def = get_sxpr_type(get_type(x)); + + if(def){ + if(def->free){ + def->free(x); + } else if (def->pointer){ + hfree(x); + } + } +} + +/** General sxpr equality function. + * Compares x and y using the equal function for x. + * Uses default_equal() if x has no equal function. + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int objequal(Sxpr x, Sxpr y){ + SxprType *def = get_sxpr_type(get_type(x)); + ObjEqualFn *equal_fn = (def && def->equal ? def->equal : default_equal); + return equal_fn(x, y); +} + +/** Search for a key in an alist. + * An alist is a list of conses, where the cars + * of the conses are the keys. Compares keys using equality. + * + * @param k key + * @param l alist to search + * @return first element of l with car k, or ONULL + */ +Sxpr assoc(Sxpr k, Sxpr l){ + for( ; CONSP(l) ; l = CDR(l)){ + Sxpr x = CAR(l); + if(CONSP(x) && objequal(k, CAR(x))){ + return x; + } + } + return ONULL; +} + +/** Search for a key in an alist. + * An alist is a list of conses, where the cars + * of the conses are the keys. Compares keys using eq. + * + * @param k key + * @param l alist to search + * @return first element of l with car k, or ONULL + */ +Sxpr assocq(Sxpr k, Sxpr l){ + for( ; CONSP(l); l = CDR(l)){ + Sxpr x = CAR(l); + if(CONSP(x) && eq(k, CAR(x))){ + return x; + } + } + return ONULL; +} + +/** Add a new key and value to an alist. + * + * @param k key + * @param l value + * @param l alist + * @return l with the new cell added to the front + */ +Sxpr acons(Sxpr k, Sxpr v, Sxpr l){ + Sxpr x, y; + x = cons_new(k, v); + if(NOMEMP(x)) return x; + y = cons_new(x, l); + if(NOMEMP(y)) cons_free_cells(x); + return y; +} + +/** Test if a list contains an element. + * Uses sxpr equality. + * + * @param l list + * @param x element to look for + * @return a tail of l with x as car, or ONULL + */ +Sxpr cons_member(Sxpr l, Sxpr x){ + for( ; CONSP(l) && !eq(x, CAR(l)); l = CDR(l)){} + return l; +} + +/** Test if a list contains an element satisfying a test. + * The test function is called with v and an element of the list. + * + * @param l list + * @param test_fn test function to use + * @param v value for first argument to the test + * @return a tail of l with car satisfying the test, or 0 + */ +Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){ + for( ; CONSP(l) && !test_fn(v, CAR(l)); l = CDR(l)){ } + return l; +} + +/** Test if the elements of list 't' are a subset of the elements + * of list 's'. Element order is not significant. + * + * @param s element list to check subset of + * @param t element list to check if is a subset + * @return 1 if is a subset, 0 otherwise + */ +int cons_subset(Sxpr s, Sxpr t){ + for( ; CONSP(t); t = CDR(t)){ + if(!CONSP(cons_member(s, CAR(t)))){ + return 0; + } + } + return 1; +} + +/** Test if two lists have equal sets of elements. + * Element order is not significant. + * + * @param s list to check + * @param t list to check + * @return 1 if equal, 0 otherwise + */ +int cons_set_equal(Sxpr s, Sxpr t){ + return cons_subset(s, t) && cons_subset(t, s); +} + +#ifdef USE_GC +/*============================================================================*/ +/* The functions inside this ifdef are only safe if GC is used. + * Otherwise they may leak memory. + */ + +/** Remove an element from a list (GC only). + * Uses sxpr equality and removes all instances, even + * if there are more than one. + * + * @param l list to remove elements from + * @param x element to remove + * @return modified input list + */ +Sxpr cons_remove(Sxpr l, Sxpr x){ + return cons_remove_if(l, eq, x); +} + +/** Remove elements satisfying a test (GC only). + * The test function is called with v and an element of the set. + * + * @param l list to remove elements from + * @param test_fn function to use to decide if an element should be removed + * @return modified input list + */ +Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v){ + Sxpr prev = ONULL, elt, next; + + for(elt = l; CONSP(elt); elt = next){ + next = CDR(elt); + if(test_fn(v, CAR(elt))){ + if(NULLP(prev)){ + l = next; + } else { + CDR(prev) = next; + } + } + } + return l; +} + +/** Set the value for a key in an alist (GC only). + * If the key is present, changes the value, otherwise + * adds a new cell. + * + * @param k key + * @param v value + * @param l alist + * @return modified or extended list + */ +Sxpr setf(Sxpr k, Sxpr v, Sxpr l){ + Sxpr e = assoc(k, l); + if(NULLP(e)){ + l = acons(k, v, l); + } else { + CAR(CDR(e)) = v; + } + return l; +} +/*============================================================================*/ +#endif /* USE_GC */ + +/** Create a new atom with the given name. + * + * @param name the name + * @return new atom + */ +Sxpr atom_new(char *name){ + Sxpr n, obj = ONOMEM; + + n = string_new(name); + if(NOMEMP(n)) goto exit; + obj = HALLOC(ObjAtom, T_ATOM); + if(NOMEMP(obj)) goto exit; + OBJ_ATOM(obj)->name = n; + exit: + return obj; +} + +/** Free an atom. + * + * @param obj to free + */ +void atom_free(Sxpr obj){ + // Interned atoms are shared, so do not free. + if(OBJ_ATOM(obj)->interned) return; + objfree(OBJ_ATOM(obj)->name); + hfree(obj); +} + +/** Print an atom. Prints the atom name. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes printed + */ +int atom_print(IOStream *io, Sxpr obj, unsigned flags){ + //return string_print(io, OBJ_ATOM(obj)->name, (flags | PRINT_RAW)); + return string_print(io, OBJ_ATOM(obj)->name, flags); +} + +/** Atom equality. + * + * @param x to compare + * @param y to compare + * @return 1 if equal, 0 otherwise + */ +int atom_equal(Sxpr x, Sxpr y){ + int ok; + ok = eq(x, y); + if(ok) goto exit; + ok = ATOMP(y) && string_equal(OBJ_ATOM(x)->name, OBJ_ATOM(y)->name); + if(ok) goto exit; + ok = STRINGP(y) && string_equal(OBJ_ATOM(x)->name, y); + exit: + return ok; +} + +/** Get the name of an atom. + * + * @param obj atom + * @return name + */ +char * atom_name(Sxpr obj){ + return string_string(OBJ_ATOM(obj)->name); +} + +/** Get the C string from a string sxpr. + * + * @param obj string sxpr + * @return string + */ +char * string_string(Sxpr obj){ + return OBJ_STRING(obj); +} + +/** Get the length of a string. + * + * @param obj string + * @return length + */ +int string_length(Sxpr obj){ + return strlen(OBJ_STRING(obj)); +} + +/** Create a new string. The input string is copied, + * and must be null-terminated. + * + * @param s characters to put in the string + * @return new sxpr + */ +Sxpr string_new(char *s){ + int n = (s ? strlen(s) : 0); + Sxpr obj; + obj = halloc(n+1, T_STRING); + if(!NOMEMP(obj)){ + char *str = OBJ_STRING(obj); + strncpy(str, s, n); + str[n] = '\0'; + } + return obj; +} + +/** Free a string. + * + * @param obj to free + */ +void string_free(Sxpr obj){ + hfree(obj); +} + +/** Determine if a string needs escapes when printed + * using the given flags. + * + * @param str string to check + * @param flags print flags + * @return 1 if needs escapes, 0 otherwise + */ +int needs_escapes(char *str, unsigned flags){ + char *c; + int val = 0; + + if(str){ + for(c=str; *c; c++){ + if(in_alpha_class(*c)) continue; + if(in_decimal_digit_class(*c)) continue; + if(in_class(*c, "/._+:@~-")) continue; + val = 1; + break; + } + } + //printf("\n> val=%d str=|%s|\n", val, str); + return val; +} + +/** Print a string to a stream, with escapes if necessary. + * + * @param io stream to print to + * @param str string + * @param flags print flags + * @return number of bytes written + */ +int _string_print(IOStream *io, char *str, unsigned flags){ + int k = 0; + if((flags & PRINT_RAW) || !needs_escapes(str, flags)){ + k += IOStream_print(io, str); + } else { + k += IOStream_print(io, "\""); + if(str){ + char *s; + for(s = str; *s; s++){ + if(*s < ' ' || *s >= 127 ){ + switch(*s){ + case '\a': k += IOStream_print(io, "\\a"); break; + case '\b': k += IOStream_print(io, "\\b"); break; + case '\f': k += IOStream_print(io, "\\f"); break; + case '\n': k += IOStream_print(io, "\\n"); break; + case '\r': k += IOStream_print(io, "\\r"); break; + case '\t': k += IOStream_print(io, "\\t"); break; + case '\v': k += IOStream_print(io, "\\v"); break; + default: + // Octal escape; + k += IOStream_print(io, "\\%o", *s); + break; + } + } else if(*s == c_double_quote || + *s == c_single_quote || + *s == c_escape){ + k += IOStream_print(io, "\\%c", *s); + } else { + k+= IOStream_print(io, "%c", *s); + } + } + } + k += IOStream_print(io, "\""); + } + return k; +} + +/** Print a string to a stream, with escapes if necessary. + * + * @param io stream to print to + * @param obj string + * @param flags print flags + * @return number of bytes written + */ +int string_print(IOStream *io, Sxpr obj, unsigned flags){ + return _string_print(io, OBJ_STRING(obj), flags); +} + +/** Compare an sxpr with a string for equality. + * + * @param x string to compare with + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int string_equal(Sxpr x, Sxpr y){ + int ok = 0; + ok = eq(x,y); + if(ok) goto exit; + ok = has_type(y, T_STRING) && !strcmp(OBJ_STRING(x), OBJ_STRING(y)); + if(ok) goto exit; + ok = has_type(y, T_ATOM) && !strcmp(OBJ_STRING(x), atom_name(y)); + exit: + return ok; +} + +/** Create a new cons cell. + * The cell is ONOMEM if either argument is. + * + * @param car sxpr for the car + * @param cdr sxpr for the cdr + * @return new cons + */ +Sxpr cons_new(Sxpr car, Sxpr cdr){ + Sxpr obj; + if(NOMEMP(car) || NOMEMP(cdr)){ + obj = ONOMEM; + } else { + obj = HALLOC(ObjCons, T_CONS); + if(!NOMEMP(obj)){ + ObjCons *z = OBJ_CONS(obj); + z->car = car; + z->cdr = cdr; + } + } + return obj; +} + +/** Push a new element onto a list. + * + * @param list list to add to + * @param elt element to add + * @return 0 if successful, error code otherwise + */ +int cons_push(Sxpr *list, Sxpr elt){ + Sxpr l; + l = cons_new(elt, *list); + if(NOMEMP(l)) return -ENOMEM; + *list = l; + return 0; +} + +/** Free a cons. Recursively frees the car and cdr. + * + * @param obj to free + */ +void cons_free(Sxpr obj){ + Sxpr next; + for(; CONSP(obj); obj = next){ + next = CDR(obj); + objfree(CAR(obj)); + hfree(obj); + } + if(!NULLP(obj)){ + objfree(obj); + } +} + +/** Free a cons and its cdr cells, but not the car sxprs. + * Does nothing if called on something that is not a cons. + * + * @param obj to free + */ +void cons_free_cells(Sxpr obj){ + Sxpr next; + for(; CONSP(obj); obj = next){ + next = CDR(obj); + hfree(obj); + } +} + +/** Print a cons. + * Prints the cons in list format if the cdrs are conses. + * uses pair (dot) format if the last cdr is not a cons (or null). + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +int cons_print(IOStream *io, Sxpr obj, unsigned flags){ + int first = 1; + int k = 0; + k += IOStream_print(io, "("); + for( ; CONSP(obj) ; obj = CDR(obj)){ + if(first){ + first = 0; + } else { + k += IOStream_print(io, " "); + } + k += objprint(io, CAR(obj), flags); + } + if(!NULLP(obj)){ + k += IOStream_print(io, " . "); + k += objprint(io, obj, flags); + } + k += IOStream_print(io, ")"); + return (IOStream_error(io) ? -1 : k); +} + +/** Compare a cons with another sxpr for equality. + * If y is a cons, compares the cars and cdrs recursively. + * + * @param x cons to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +int cons_equal(Sxpr x, Sxpr y){ + return CONSP(y) && + objequal(CAR(x), CAR(y)) && + objequal(CDR(x), CDR(y)); +} + +/** Return the length of a cons list. + * + * @param obj list + * @return length + */ +int cons_length(Sxpr obj){ + int count = 0; + for( ; CONSP(obj); obj = CDR(obj)){ + count++; + } + return count; +} + +/** Destructively reverse a cons list in-place. + * If the argument is not a cons it is returned unchanged. + * + * @param l to reverse + * @return reversed list + */ +Sxpr nrev(Sxpr l){ + if(CONSP(l)){ + // Iterate down the cells in the list making the cdr of + // each cell point to the previous cell. The last cell + // is the head of the reversed list. + Sxpr prev = ONULL; + Sxpr cell = l; + Sxpr next; + + while(1){ + next = CDR(cell); + CDR(cell) = prev; + if(!CONSP(next)) break; + prev = cell; + cell = next; + } + l = cell; + } + return l; +} + +/** Print the null sxpr. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int null_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, "()"); +} + +/** Print the `unspecified' sxpr none. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int none_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, ""); +} + +/** Print an integer. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int int_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, "%d", OBJ_INT(obj)); +} + +/** Print a boolean. + * + * @param io stream to print to + * @param obj to print + * @param flags print flags + * @return number of bytes written + */ +static int bool_print(IOStream *io, Sxpr obj, unsigned flags){ + return IOStream_print(io, (OBJ_UINT(obj) ? k_true : k_false)); +} + +int sxprp(Sxpr obj, Sxpr name){ + return CONSP(obj) && objequal(CAR(obj), name); +} + +/** Get the name of an element. + * + * @param obj element + * @return name + */ +Sxpr sxpr_name(Sxpr obj){ + Sxpr val = ONONE; + if(CONSP(obj)){ + val = CAR(obj); + } else if(STRINGP(obj) || ATOMP(obj)){ + val = obj; + } + return val; +} + +int sxpr_is(Sxpr obj, char *s){ + if(ATOMP(obj)) return !strcmp(atom_name(obj), s); + if(STRINGP(obj)) return !strcmp(string_string(obj), s); + return 0; +} + +int sxpr_elementp(Sxpr obj, Sxpr name){ + int ok = 0; + ok = CONSP(obj) && objequal(CAR(obj), name); + return ok; +} + +/** Get the attributes of an sxpr. + * + * @param obj sxpr + * @return attributes + */ +Sxpr sxpr_attributes(Sxpr obj){ + Sxpr val = ONULL; + if(CONSP(obj)){ + obj = CDR(obj); + if(CONSP(obj)){ + obj = CAR(obj); + if(sxprp(obj, intern("@"))){ + val = CDR(obj); + } + } + } + return val; +} + +Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def){ + Sxpr val = ONONE; + val = assoc(sxpr_attributes(obj), key); + if(CONSP(val) && CONSP(CDR(val))){ + val = CADR(def); + } else { + val = def; + } + return val; +} + +/** Get the children of an sxpr. + * + * @param obj sxpr + * @return children + */ +Sxpr sxpr_children(Sxpr obj){ + Sxpr val = ONULL; + if(CONSP(obj)){ + val = CDR(obj); + if(CONSP(val) && sxprp(CAR(val), intern("@"))){ + val = CDR(val); + } + } + return val; +} + +Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def){ + Sxpr val = ONONE; + Sxpr l; + for(l = sxpr_children(obj); CONSP(l); l = CDR(l)){ + if(sxprp(CAR(l), name)){ + val = CAR(l); + break; + } + } + if(NONEP(val)) val = def; + return val; +} + +Sxpr sxpr_child0(Sxpr obj, Sxpr def){ + Sxpr val = ONONE; + Sxpr l = sxpr_children(obj); + if(CONSP(l)){ + val = CAR(l); + } else { + val = def; + } + return val; +} + +Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def){ + Sxpr val = def; + Sxpr l; + int i; + for (i = 0, l = sxpr_children(obj); CONSP(l); i++, l = CDR(l)){ + if(i == n){ + val = CAR(l); + break; + } + } + return val; +} + +Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def){ + Sxpr val = ONONE; + val = sxpr_child(obj, name, ONONE); + if(NONEP(val)){ + val = def; + } else { + val = sxpr_child0(val, def); + } + return val; +} + +/** Table of interned symbols. Indexed by symbol name. */ +static HashTable *symbols = NULL; + +/** Hash function for entries in the symbol table. + * + * @param key to hash + * @return hashcode + */ +static Hashcode sym_hash_fn(void *key){ + return hash_string((char*)key); +} + +/** Key equality function for the symbol table. + * + * @param x to compare + * @param y to compare + * @return 1 if equal, 0 otherwise + */ +static int sym_equal_fn(void *x, void *y){ + return !strcmp((char*)x, (char*)y); +} + +/** Entry free function for the symbol table. + * + * @param table the entry is in + * @param entry being freed + */ +static void sym_free_fn(HashTable *table, HTEntry *entry){ + if(entry){ + objfree(((ObjAtom*)entry->value)->name); + HTEntry_free(entry); + } +} + +/** Initialize the symbol table. + * + * @return 0 on sucess, error code otherwise + */ +static int init_symbols(void){ + symbols = HashTable_new(100); + if(symbols){ + symbols->key_hash_fn = sym_hash_fn; + symbols->key_equal_fn = sym_equal_fn; + symbols->entry_free_fn = sym_free_fn; + return 0; + } + return -1; +} + +/** Cleanup the symbol table. Frees the table and all its symbols. + */ +void cleanup_symbols(void){ + HashTable_free(symbols); + symbols = NULL; +} + +/** Get the interned symbol with the given name. + * No new symbol is created. + * + * @return symbol or null + */ +Sxpr get_symbol(char *sym){ + HTEntry *entry; + if(!symbols){ + if(init_symbols()) return ONOMEM; + return ONULL; + } + entry = HashTable_get_entry(symbols, sym); + if(entry){ + return OBJP(T_ATOM, entry->value); + } else { + return ONULL; + } +} + +/** Get the interned symbol with the given name. + * Creates a new symbol if necessary. + * + * @return symbol + */ +Sxpr intern(char *sym){ + Sxpr symbol = get_symbol(sym); + if(NULLP(symbol)){ + if(!symbols) return ONOMEM; + symbol = atom_new(sym); + if(!NOMEMP(symbol)){ + OBJ_ATOM(symbol)->interned = TRUE; + HashTable_add(symbols, atom_name(symbol), get_ptr(symbol)); + } + } + return symbol; +} diff --git a/tools/xfrd/sxpr.h b/tools/xfrd/sxpr.h new file mode 100644 index 0000000000..a5875c76bd --- /dev/null +++ b/tools/xfrd/sxpr.h @@ -0,0 +1,414 @@ +/* + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of the + * License, or (at your option) any later version. This library is + * distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _XUTIL_SXPR_H_ +#define _XUTIL_SXPR_H_ + +#include + +#include "hash_table.h" +#include "iostream.h" +#include "allocate.h" + +/** @file + * Definitions for rules and sxprs. + */ + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +/** Sxpr type. */ +typedef int16_t TypeCode; + +/** A typed sxpr handle.*/ +typedef struct Sxpr { + /** Sxpr type. */ + TypeCode type; + union { + /** Sxpr value. */ + unsigned long ul; + /** Pointer. */ + void *ptr; + } v; +} Sxpr; + +/** Sxpr type to indicate out of memory. */ +#define T_NOMEM ((TypeCode)-1) +/** The 'unspecified' sxpr. */ +#define T_NONE ((TypeCode)0) +/** The empty list. */ +#define T_NULL ((TypeCode)1) +/** Unsigned integer. */ +#define T_UINT ((TypeCode)2) +/** A string. */ +#define T_STRING ((TypeCode)3) +/** An atom. */ +#define T_ATOM ((TypeCode)4) +/** A boolean. */ +#define T_BOOL ((TypeCode)5) + +/** A cons (pair or list). */ +#define T_CONS ((TypeCode)10) + +/** An error. */ +#define T_ERR ((TypeCode)40) + +/** An atom. */ +typedef struct ObjAtom { + Sxpr name; + Hashcode hashcode; + int interned; +} ObjAtom; + +/** A cons (pair). */ +typedef struct ObjCons { + Sxpr car; + Sxpr cdr; +} ObjCons; + +/** A vector. */ +typedef struct ObjVector { + int n; + Sxpr data[0]; +} ObjVector; + +/** Flags for sxpr printing. */ +enum PrintFlags { + PRINT_RAW = 0x001, + PRINT_TYPE = 0x002, + PRINT_PRETTY = 0x004, + PRINT_NUM = 0x008, +}; + +/** An integer sxpr. + * + * @param ty type + * @param val integer value + */ +#define OBJI(ty, val) (Sxpr){ type: (ty), v: { ul: (val) }} + +/** A pointer sxpr. + * If the pointer is non-null, returns an sxpr containing it. + * If the pointer is null, returns ONOMEM. + * + * @param ty type + * @param val pointer + */ +#define OBJP(ty, val) ((val) ? (Sxpr){ type: (ty), v: { ptr: (val) }} : ONOMEM) + +/** Make an integer sxpr containing a pointer. + * + * @param val pointer + */ +#define PTR(val) OBJP(T_UINT, (void*)(val)) + +/** Make an integer sxpr. + * @param x value + */ +#define OINT(x) OBJI(T_UINT, x) + +/** Make an error sxpr. + * + * @param x value + */ +#define OERR(x) OBJI(T_ERR, x) + +/** Out of memory constant. */ +#define ONOMEM OBJI(T_NOMEM, 0) + +/** The `unspecified' constant. */ +#define ONONE OBJI(T_NONE, 0) + +/** Empty list constant. */ +#define ONULL OBJI(T_NULL, 0) + +/** False constant. */ +#define OFALSE OBJI(T_BOOL, 0) + +/** True constant. */ +#define OTRUE OBJI(T_BOOL, 1) + +/* Recognizers for the various sxpr types. */ +#define ATOMP(obj) has_type(obj, T_ATOM) +#define BOOLP(obj) has_type(obj, T_BOOL) +#define CONSP(obj) has_type(obj, T_CONS) +#define ERRP(obj) has_type(obj, T_ERR) +#define INTP(obj) has_type(obj, T_UINT) +#define NOMEMP(obj) has_type(obj, T_NOMEM) +#define NONEP(obj) has_type(obj, T_NONE) +#define NULLP(obj) has_type(obj, T_NULL) +#define STRINGP(obj) has_type(obj, T_STRING) + +#define TRUEP(obj) get_ul(obj) + +/** Convert an sxpr to an unsigned integer. */ +#define OBJ_UINT(x) get_ul(x) +/** Convert an sxpr to an integer. */ +#define OBJ_INT(x) (int)get_ul(x) + +/* Conversions of sxprs to their values. + * No checking is done. + */ +#define OBJ_STRING(x) ((char*)get_ptr(x)) +#define OBJ_CONS(x) ((ObjCons*)get_ptr(x)) +#define OBJ_ATOM(x) ((ObjAtom*)get_ptr(x)) +#define OBJ_SET(x) ((ObjSet*)get_ptr(x)) +#define CAR(x) (OBJ_CONS(x)->car) +#define CDR(x) (OBJ_CONS(x)->cdr) + +#define CAAR(x) (CAR(CAR(x))) +#define CADR(x) (CAR(CDR(x))) +#define CDAR(x) (CDR(CAR(x))) +#define CDDR(x) (CDR(CDR(x))) + +/** Get the integer value from an sxpr. + * + * @param obj sxpr + * @return value + */ +static inline unsigned long get_ul(Sxpr obj){ + return obj.v.ul; +} + +/** Get the pointer value from an sxpr. + * + * @param obj sxpr + * @return value + */ +static inline void * get_ptr(Sxpr obj){ + return obj.v.ptr; +} + +/** Create an sxpr containing a pointer. + * + * @param type typecode + * @param val pointer + * @return sxpr + */ +static inline Sxpr obj_ptr(TypeCode type, void *val){ + return (Sxpr){ type: type, v: { ptr: val } }; +} + +/** Create an sxpr containing an integer. + * + * @param type typecode + * @param val integer + * @return sxpr + */ +static inline Sxpr obj_ul(TypeCode type, unsigned long val){ + return (Sxpr){ type: type, v: { ul: val } }; +} + +/** Get the type of an sxpr. + * + * @param obj sxpr + * @return type + */ +static inline TypeCode get_type(Sxpr obj){ + return obj.type; +} + +/** Check the type of an sxpr. + * + * @param obj sxpr + * @param type to check + * @return 1 if has the type, 0 otherwise + */ +static inline int has_type(Sxpr obj, TypeCode type){ + return get_type(obj) == type; +} + +/** Compare sxprs for literal equality of type and value. + * + * @param x sxpr to compare + * @param y sxpr to compare + * @return 1 if equal, 0 otherwise + */ +static inline int eq(Sxpr x, Sxpr y){ + return ((get_type(x) == get_type(y)) && (get_ul(x) == get_ul(y))); +} + +/** Checked version of CAR + * + * @param x sxpr + * @return CAR if a cons, x otherwise + */ +static inline Sxpr car(Sxpr x){ + return (CONSP(x) ? CAR(x) : x); +} + +/** Checked version of CDR. + * + * @param x sxpr + * @return CDR if a cons, null otherwise + */ +static inline Sxpr cdr(Sxpr x){ + return (CONSP(x) ? CDR(x) : ONULL); +} + +/** Allocate some memory and return an sxpr containing it. + * Returns ONOMEM if allocation failed. + * + * @param n number of bytes to allocate + * @param ty typecode + * @return sxpr + */ +static inline Sxpr halloc(size_t n, TypeCode ty){ + return OBJP(ty, allocate(n)); +} + +/** Allocate an sxpr containing a pointer to the given type. + * + * @param ty type (uses sizeof to determine how many bytes to allocate) + * @param code typecode + * @return sxpr, ONOMEM if allocation failed + */ +#define HALLOC(ty, code) halloc(sizeof(ty), code) + +typedef int ObjPrintFn(IOStream *io, Sxpr obj, unsigned flags); +typedef int ObjEqualFn(Sxpr obj, Sxpr other); +typedef void ObjFreeFn(Sxpr obj); + +/** An sxpr type definition. */ +typedef struct SxprType { + TypeCode type; + char *name; + int pointer; + ObjPrintFn *print; + ObjEqualFn *equal; + ObjFreeFn *free; +} SxprType; + + +extern SxprType *get_sxpr_type(int ty); + +/** Free the pointer in an sxpr. + * + * @param x sxpr containing a pointer + */ +static inline void hfree(Sxpr x){ + deallocate(get_ptr(x)); +} + +extern int objprint(IOStream *io, Sxpr x, unsigned flags); +extern int objequal(Sxpr x, Sxpr y); +extern void objfree(Sxpr x); + +extern void cons_free_cells(Sxpr obj); +extern Sxpr intern(char *s); + +extern Sxpr assoc(Sxpr k, Sxpr l); +extern Sxpr assocq(Sxpr k, Sxpr l); +extern Sxpr acons(Sxpr k, Sxpr v, Sxpr l); +extern Sxpr nrev(Sxpr l); +extern Sxpr cons_member(Sxpr l, Sxpr x); +extern Sxpr cons_member_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v); +extern int cons_subset(Sxpr s, Sxpr t); +extern int cons_set_equal(Sxpr s, Sxpr t); + +#ifdef USE_GC +extern Sxpr cons_remove(Sxpr l, Sxpr x); +extern Sxpr cons_remove_if(Sxpr l, ObjEqualFn *test_fn, Sxpr v); +#endif + +extern Sxpr atom_new(char *name); +extern char * atom_name(Sxpr obj); + +extern Sxpr string_new(char *s); +extern char * string_string(Sxpr obj); +extern int string_length(Sxpr obj); + +extern Sxpr cons_new(Sxpr car, Sxpr cdr); +extern int cons_push(Sxpr *list, Sxpr elt); +extern int cons_length(Sxpr obj); + +Sxpr sxpr_name(Sxpr obj); +int sxpr_is(Sxpr obj, char *s); +int sxpr_elementp(Sxpr obj, Sxpr name); +Sxpr sxpr_attributes(Sxpr obj); +Sxpr sxpr_attribute(Sxpr obj, Sxpr key, Sxpr def); +Sxpr sxpr_children(Sxpr obj); +Sxpr sxpr_child(Sxpr obj, Sxpr name, Sxpr def); +Sxpr sxpr_childN(Sxpr obj, int n, Sxpr def); +Sxpr sxpr_child0(Sxpr obj, Sxpr def); +Sxpr sxpr_child_value(Sxpr obj, Sxpr name, Sxpr def); + +/** Create a new atom. + * + * @param s atom name + * @return new atom + */ +static inline Sxpr mkatom(char *s){ + return atom_new(s); +} + +/** Create a new string sxpr. + * + * @param s string bytes (copied) + * @return new string + */ +static inline Sxpr mkstring(char *s){ + return string_new(s); +} + +/** Create an integer sxpr. + * + * @param i value + * @return sxpr + */ +static inline Sxpr mkint(int i){ + return OBJI(T_UINT, i); +} + +/** Create a boolean sxpr. + * + * @param b value + * @return sxpr + */ +static inline Sxpr mkbool(int b){ + return OBJI(T_BOOL, (b ? 1 : 0)); +} + +/* Constants used in parsing and printing. */ +#define k_list_open "(" +#define c_list_open '(' +#define k_list_close ")" +#define c_list_close ')' +#define k_true "true" +#define k_false "false" + +#define c_var '$' +#define c_escape '\\' +#define c_single_quote '\'' +#define c_double_quote '"' +#define c_string_open c_double_quote +#define c_string_close c_double_quote +#define c_data_open '[' +#define c_data_close ']' +#define c_binary '*' +#define c_eval '!' +#define c_concat_open '{' +#define c_concat_close '}' + +#endif /* ! _XUTIL_SXPR_H_ */ diff --git a/tools/xfrd/xdr.c b/tools/xfrd/xdr.c new file mode 100644 index 0000000000..61dbe44ed6 --- /dev/null +++ b/tools/xfrd/xdr.c @@ -0,0 +1,316 @@ +#include +#include "xdr.h" + +#define MODULE_NAME "XDR" +//#define DEBUG 1 +#undef DEBUG +#include "debug.h" + +/** @file + * XDR packer/unpacker for elements. + * + * string -> [T_STRING] [len:u16] + * atom -> [T_ATOM] [len:u16] + * uint -> [T_UINT] [value] + * cons -> [T_LIST] {1 elt}* 0 + * null -> [T_NULL] + * none -> [T_NONE] + * bool -> [T_BOOL] { 0:u8 | 1:u8 } + * + * types packed as u16. + * + * So (a b c) -> [T_CONS] a [T_CONS] b [T_CONS] c [T_NULL] + * () -> [T_NULL] + */ + +int pack_bool(IOStream *io, int x){ + int err=0; + //dprintf("> x=%d\n", x); + err = IOStream_print(io, "%c", 0xff & x); + if(err > 0) err = 0; + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_bool(IOStream *io, int *x){ + int err = 0; + int c; + //dprintf(">\n"); + c = IOStream_getc(io); + *x = (c < 0 ? 0 : c); + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + //dprintf("< err=%d x=%d\n", err, *x); + return err; +} + +int pack_ushort(IOStream *io, unsigned short x){ + int err=0; + //dprintf("> x=%u\n", x); + err = IOStream_print(io, "%c%c", + 0xff & (x >> 8), + 0xff & (x )); + if(err > 0) err = 0; + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_ushort(IOStream *io, unsigned short *x){ + int err = 0; + int i, c = 0; + //dprintf(">\n"); + *x = 0; + for(i = 0; i< 2; i++){ + c = IOStream_getc(io); + if(c < 0) break; + *x <<= 8; + *x |= (0xff & c); + } + err = IOStream_error(io); + + if(c < 0 && !err) err = -EIO; + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int pack_type(IOStream *io, unsigned short x){ + return pack_ushort(io, x); +} + +int unpack_type(IOStream *io, unsigned short *x){ + return unpack_ushort(io, x); +} + +int pack_uint(IOStream *io, unsigned int x){ + int err=0; + //dprintf("> x=%u\n", x); + err = IOStream_print(io, "%c%c%c%c", + 0xff & (x >> 24), + 0xff & (x >> 16), + 0xff & (x >> 8), + 0xff & (x )); + if(err > 0) err = 0; + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_uint(IOStream *io, unsigned int *x){ + int err = 0; + int i, c = 0; + //dprintf(">\n"); + *x = 0; + for(i = 0; i< 4; i++){ + c = IOStream_getc(io); + if(c < 0) break; + *x <<= 8; + *x |= (0xff & c); + } + err = IOStream_error(io); + if(c < 0 && !err) err = -EIO; + //dprintf("< err=%d x=%u\n", err, *x); + return err; +} + +int pack_string(IOStream *io, Sxpr x){ + int err = 0; + unsigned short n = 0xffff & string_length(x); + char *s = string_string(x); + int i; + //dprintf("> n=%d s=%s\n", n, s); + err = pack_ushort(io, n); + if(err) goto exit; + for(i = 0; i < n; i++){ + err = IOStream_print(io, "%c", s[i]); + if(err < 0) break; + } + if(err > 0) err = 0; + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_string(IOStream *io, Sxpr *x){ + int err; + unsigned short n; + int i, c = 0; + char *s; + Sxpr val = ONONE; + + //dprintf(">\n"); + err = unpack_ushort(io, &n); + if(err) goto exit; + val = halloc(n+1, T_STRING); + if(NOMEMP(val)){ + err = -ENOMEM; + goto exit; + } + s = string_string(val); + for(i=0; i\n"); + for(l = x; CONSP(l); l = CDR(l)){ + err = pack_bool(io, 1); + if(err) goto exit; + err = pack_sxpr(io, CAR(l)); + if(err) goto exit; + } + err = pack_bool(io, 0); + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_cons(IOStream *io, Sxpr *x){ + int err = 0; + int more = 0; + Sxpr u = ONONE, v = ONONE, val = ONULL; + + dprintf(">\n"); + while(1){ + err = unpack_bool(io, &more); + if(err) goto exit; + if(!more){ + //IOStream_print(iostdout, "unpack_cons 1 val="); + ////objprint(iostdout, val, 0); + IOStream_print(iostdout, "\n"); + + val = nrev(val); + + //IOStream_print(iostdout, "unpack_cons 2 val="); + //objprint(iostdout, val, 0); + //IOStream_print(iostdout, "\n"); + + break; + } + err = unpack_sxpr(io, &u); + if(err) goto exit; + v = cons_new(u, val); + if(NOMEMP(v)){ + err = -ENOMEM; + objfree(u); + goto exit; + } + val = v; + } + exit: + if(err){ + objfree(val); + val = ONONE; + } + *x = val; + dprintf("< err=%d\n", err); + return err; +} + +int pack_sxpr(IOStream *io, Sxpr x){ + int err = 0; + unsigned short type = get_type(x); + //dprintf(">\n"); + //objprint(iostdout, x, 0); + //IOStream_print(iostdout, "\n"); + + err = pack_type(io, type); + if(err) goto exit; + switch(type){ + case T_NULL: + break; + case T_NONE: + break; + case T_BOOL: + err = pack_bool(io, get_ul(x)); + break; + case T_CONS: + err = pack_cons(io, x); + break; + case T_ATOM: + err = pack_string(io, OBJ_ATOM(x)->name); + break; + case T_STRING: + err = pack_string(io, x); + break; + case T_UINT: + err = pack_uint(io, get_ul(x)); + break; + default: + err = -EINVAL; + IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type); + break; + } + exit: + //dprintf("< err=%d\n", err); + return err; +} + +int unpack_sxpr(IOStream *io, Sxpr *x){ + int err = 0; + unsigned short type; + unsigned int u; + Sxpr val = ONONE, y; + + //dprintf(">\n"); + err = unpack_type(io, &type); + if(err) goto exit; + switch(type){ + case T_NULL: + val = ONULL; + break; + case T_NONE: + val = ONONE; + break; + case T_CONS: + err = unpack_cons(io, &val); + break; + case T_BOOL: + err = unpack_bool(io, &u); + if(err) goto exit; + val = (u ? OTRUE : OFALSE); + break; + case T_ATOM: + err = unpack_string(io, &y); + if(err) goto exit; + val = intern(string_string(y)); + objfree(y); + break; + case T_STRING: + err = unpack_string(io, &val); + break; + case T_UINT: + err = unpack_uint(io, &u); + if(err) goto exit; + val = OBJI(type, u); + break; + default: + err = -EINVAL; + IOStream_print(iostderr, "%s> invalid type %d\n", __FUNCTION__, type); + break; + } + exit: + *x = (err ? ONONE : val); + //IOStream_print(iostdout, "sxpr="); + //objprint(iostdout, *x, 0); + //IOStream_print(iostdout, "\n"); + //dprintf("< err=%d\n", err); + return err; +} diff --git a/tools/xfrd/xdr.h b/tools/xfrd/xdr.h new file mode 100644 index 0000000000..793cd34a29 --- /dev/null +++ b/tools/xfrd/xdr.h @@ -0,0 +1,30 @@ +#ifndef _XUTIL_XDR_H_ +#define _XUTIL_XDR_H_ +#include "iostream.h" +#include "sxpr.h" + +int pack_type(IOStream *io, unsigned short x); + +int unpack_type(IOStream *io, unsigned short *x); + +int pack_bool(IOStream *io, int x); + +int unpack_bool(IOStream *io, int *x); + +int pack_uint(IOStream *out, unsigned int x); + +int unpack_uint(IOStream *in, unsigned int *x); + +int pack_string(IOStream *out, Sxpr x); + +int unpack_string(IOStream *in, Sxpr *x); + +int pack_cons(IOStream *out, Sxpr x); + +int unpack_cons(IOStream *in, Sxpr *x); + +int pack_sxpr(IOStream *out, Sxpr x); + +int unpack_sxpr(IOStream *in, Sxpr *x); + +#endif /* _XUTIL_XDR_H_ */ diff --git a/tools/xfrd/xen_domain.c b/tools/xfrd/xen_domain.c new file mode 100644 index 0000000000..5c5f7c5bef --- /dev/null +++ b/tools/xfrd/xen_domain.c @@ -0,0 +1,90 @@ +#include +#include +#include + +#ifndef _XEN_XFR_STUB_ +#include "dom0_defs.h" +#include "mem_defs.h" +#endif + +#include "xen_domain.h" +#include "marshal.h" +#include "xdr.h" + +#define MODULE_NAME "XFRD" +#define DEBUG 1 +#include "debug.h" + +/** Write domain state. + * + * At some point during this the domain is suspended, and then there's no way back. + * Even if something later goes wrong we can't restart the domain. + */ +int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n){ + int err = 0; + char buf[1024]; + int n, k, d, buf_n; + dprintf("> dom=%d\n", dom); +#ifdef _XEN_XFR_STUB_ + err = marshal_uint32(io, dom); + if(err) goto exit; + err = marshal_string(io, vmconfig, vmconfig_n); + if(err) goto exit; + n = 32 * 1024 * 1024; + buf_n = sizeof(buf); + err = marshal_uint32(io, n); + for(k = 0; k < n; k += d){ + d = n - k; + if(d > buf_n) d = buf_n; + err = marshal_bytes(io, buf, d); + if(err) goto exit; + //dprintf("> k=%d n=%d\n", k, n); + } + + exit: +#else +#endif + dprintf("< err=%d\n", err); + return err; +} + +/** Receive domain state. + * Create a new domain and store the received state into it. + */ +int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n){ + int err = 0; + char buf[1024]; + int n, k, d, buf_n; + dprintf(">\n"); +#ifdef _XEN_XFR_STUB_ + err = unmarshal_uint32(io, dom); + if(err) goto exit; + err = unmarshal_new_string(io, vmconfig, vmconfig_n); + if(err) goto exit; + err = unmarshal_uint32(io, &n); + buf_n = sizeof(buf); + for(k = 0; k < n; k += d){ + d = n - k; + if(d > buf_n) d = buf_n; + err = unmarshal_bytes(io, buf, d); + if(err) goto exit; + //dprintf("> k=%d n=%d\n", k, n); + } + exit: +#else +#endif + dprintf("< err=%d\n", err); + return err; +} + +/** Configure a new domain. Talk to xend. Use libcurl? + */ +int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n){ + int err = 0; + dprintf(">\n"); +#ifdef _XEN_XFR_STUB_ +#else +#endif + dprintf("< err=%d\n", err); + return err; +} diff --git a/tools/xfrd/xen_domain.h b/tools/xfrd/xen_domain.h new file mode 100644 index 0000000000..4618c642b7 --- /dev/null +++ b/tools/xfrd/xen_domain.h @@ -0,0 +1,15 @@ +#ifndef _XFRD_XEN_DOMAIN_H_ +#define _XFRD_XEN_DOMAIN_H_ +#include +#include +#include "connection.h" + +/** Define to use stubs. Undefine to use Xen ops. */ +//#define _XEN_XFR_STUB_ + +extern int xen_domain_snd(Conn *xend, IOStream *io, uint32_t dom, char *vmconfig, int vmconfig_n); +extern int xen_domain_rcv(IOStream *io, uint32_t *dom, char **vmconfig, int *vmconfig_n); + + +extern int xen_domain_configure(uint32_t dom, char *vmconfig, int vmconfig_n); +#endif diff --git a/tools/xfrd/xfrd.c b/tools/xfrd/xfrd.c new file mode 100644 index 0000000000..1836231075 --- /dev/null +++ b/tools/xfrd/xfrd.c @@ -0,0 +1,1127 @@ +/** @file + * XFRD - Domain Transfer Daemon for Xen. + * + * The xfrd is forked by xend to transfer a vm to a remote system. + * + * The vm is suspended, then its state and memory are transferred to the remote system. + * The remote system attempts to create a vm and copy the transferred state and memory into it, + * finally resuming the vm. If all is OK the vm ends up running on the remote + * system and is removed from the originating system. If the transfer does not complete + * successfully the originating system attempts to resume the vm. + * The children exit when the transfer completes. + * + * @author Mike Wray + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "allocate.h" +#include "file_stream.h" +#include "string_stream.h" +#include "lzi_stream.h" +#include "sys_net.h" +#include "sys_string.h" + +#include "xdr.h" +#include "enum.h" +#include "xfrd.h" + +#include "xen_domain.h" + +#include "connection.h" +#include "select.h" + +#define MODULE_NAME "XFRD" +#define DEBUG 1 +#include "debug.h" + +/* +sender: + xend connects to xfrd and writes migrate message + xend writes domain config to xfrd + + xfrd forks + + xfrd connects to peer + xfrd sends hello, reads response + xfrd sends domain + xfrd reads response + reports progress/status to xend + + xend reads xfrd for progress/status, disconnects + If ok, destroys domain. + If not ok, unpauses domain. + +receiver: + xfrd accepts connection on inbound port + xfrd forks and accepts connection + xfrd receives hello, writes response + xfrd receives domain + xfrd connects to xend, configures new domain + xfrd writes status back to peer, child exits + + + (xfr.hello ) + (xfr.err ) + + xend->xfrd (xfr.migrate ) + (xfr.save ) + xfrd->xend (xfr.suspend ) + xfrd->xend (xfr.progress ) + xfrd->xend (xfr.err ) | (xfr.ok ) + xfrd->xfrd (xfr.xfr ) + xfrd->xfrd (xfr.err ) | (xfr.ok ) + + xfrd->xend (xfr.configure ) + */ + +Sxpr oxfr_configure; // (xfr.configure ) +Sxpr oxfr_err; // (xfr.err ) +Sxpr oxfr_hello; // (xfr.hello ) +Sxpr oxfr_migrate; // (xfr.migrate ) +Sxpr oxfr_ok; // (xfr.ok ) +Sxpr oxfr_progress; // (xfr.progress ) +Sxpr oxfr_save; // (xfr.save ) +Sxpr oxfr_suspend; // (xfr.suspend ) +Sxpr oxfr_xfr; // (xfr.xfr ) + +void xfr_init(void){ + oxfr_configure = intern("xfr.configure"); + oxfr_err = intern("xfr.err"); + oxfr_hello = intern("xfr.hello"); + oxfr_migrate = intern("xfr.migrate"); + oxfr_ok = intern("xfr.ok"); + oxfr_progress = intern("xfr.progress"); + oxfr_save = intern("xfr.save"); + oxfr_suspend = intern("xfr.suspend"); + oxfr_xfr = intern("xfr.xfr"); +} + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define PROGRAM "xfrd" + +#define OPT_PORT 'P' +#define KEY_PORT "port" +#define DOC_PORT "\n\txfr port (as a number or service name)" + +#define OPT_COMPRESS 'Z' +#define KEY_COMPRESS "compress" +#define DOC_COMPRESS "\n\tuse compression for migration" + +#define OPT_HELP 'h' +#define KEY_HELP "help" +#define DOC_HELP "\n\tprint help" + +#define OPT_VERSION 'v' +#define KEY_VERSION "version" +#define DOC_VERSION "\n\tprint version" + +#define OPT_VERBOSE 'V' +#define KEY_VERBOSE "verbose" +#define DOC_VERBOSE "\n\tverbose flag" + +/** Print a usage message. + * Prints to stdout if err is zero, and exits with 0. + * Prints to stderr if err is non-zero, and exits with 1. + */ +void usage(int err){ + FILE *out = (err ? stderr : stdout); + + fprintf(out, "Usage: %s [options]\n", PROGRAM); + fprintf(out, "-%c, --%s %s\n", OPT_PORT, KEY_PORT, DOC_PORT); + fprintf(out, "-%c, --%s %s\n", OPT_COMPRESS, KEY_COMPRESS, DOC_COMPRESS); + fprintf(out, "-%c, --%s %s\n", OPT_VERBOSE, KEY_VERBOSE, DOC_VERBOSE); + fprintf(out, "-%c, --%s %s\n", OPT_VERSION, KEY_VERSION, DOC_VERSION); + fprintf(out, "-%c, --%s %s\n", OPT_HELP, KEY_HELP, DOC_HELP); + exit(err ? 1 : 0); +} + +/** Short options. Options followed by ':' take an argument. */ +static char *short_opts = (char[]){ + OPT_PORT, ':', + OPT_COMPRESS, + OPT_HELP, + OPT_VERSION, + OPT_VERBOSE, + 0 }; + +/** Long options. */ +static struct option const long_opts[] = { + { KEY_PORT, required_argument, NULL, OPT_PORT }, + { KEY_COMPRESS, no_argument, NULL, OPT_COMPRESS }, + { KEY_HELP, no_argument, NULL, OPT_HELP }, + { KEY_VERSION, no_argument, NULL, OPT_VERSION }, + { KEY_VERBOSE, no_argument, NULL, OPT_VERBOSE }, + { NULL, 0, NULL, 0 } +}; + +typedef struct Args { + int bufsize; + unsigned long port; + int verbose; + int compress; +} Args; + +/** Transfer states. */ +enum { + XFR_INIT, + XFR_HELLO, + XFR_STATE, + XFR_RUN, + XFR_FAIL, + XFR_DONE, + XFR_MAX +}; + +/** Initialize an array element for a constant to its string name. */ +#define VALDEF(val) { val, #val } + +/** Names for the transfer states. */ +static EnumDef xfr_states[] = { + VALDEF(XFR_INIT), + VALDEF(XFR_HELLO), + VALDEF(XFR_STATE), + VALDEF(XFR_RUN), + VALDEF(XFR_FAIL), + VALDEF(XFR_DONE), + { 0, NULL } +}; + + +/** State machine for transfer. */ +typedef struct XfrState { + /** Current state. */ + int state; + /** Error codes for the states. */ + int state_err[XFR_MAX]; + /** First error. */ + int err; + /** State when first error happened. */ + int err_state; + + uint32_t vmid; + char* vmconfig; + int vmconfig_n; + unsigned long xfr_port; + char *xfr_host; + uint32_t vmid_new; +} XfrState; + +/** Get the name of a transfer state. + * + * @param s state + * @return name + */ +char * xfr_state_name(int s){ + return enum_val_to_name(s, xfr_states); +} + +/** Set the state of a transfer. + * + * @param s transfer + * @param state state + * @return state + */ +int XfrState_set_state(XfrState *s, int state){ + s->state = state; + return s->state; +} + +/** Get the state of a transfer. + * + * @param s transfer + * @return state + */ +int XfrState_get_state(XfrState *s){ + return s->state; +} + +/** Set an error in the current state. + * Does nothing if an error is already set. + * + * @param s transfer + * @param err error + * @return error + */ +int XfrState_set_err(XfrState *s, int err){ + if(!s->state_err[s->state]){ + s->state_err[s->state] = err; + } + if(!s->err){ + s->err = err; + s->err_state = s->state; + } + return err; +} + +/** Get the error in the current state. + * + * @param s transfer + * @return error + */ +int XfrState_get_err(XfrState *s){ + return s->state_err[s->state]; +} + +/** Get the first error of a transfer. + * + * @param s transfer + * @return error + */ +int XfrState_first_err(XfrState *s){ + return s->err; +} + +/** Get the state a transfer was in when it had its first error. + * + * @param s transfer + * @return error state + */ +int XfrState_first_err_state(XfrState *s){ + return s->err_state; +} + +/** Xfrd arguments. */ +static Args _args = {}; + +/** Xfrd arguments. */ +static Args *args = &_args; + +/** Set xfrd default arguments. + * + * @param args arguments to set + */ +void set_defaults(Args *args){ + args->compress = FALSE; + args->bufsize = 128 * 1024; + args->port = htons(XFRD_PORT); +} + +int stringof(Sxpr exp, char **s){ + int err = 0; + dprintf(">\n"); + objprint(iostdout, exp, PRINT_TYPE); IOStream_print(iostdout, "\n"); + if(ATOMP(exp)){ + *s = atom_name(exp); + } else if(STRINGP(exp)){ + *s = string_string(exp); + } else { + err = -EINVAL; + *s = NULL; + } + dprintf("< err=%d s=%s\n", err, *s); + return err; +} + +int intof(Sxpr exp, int *v){ + int err = 0; + char *s; + unsigned long l; + dprintf(">\n"); + objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n"); + if(INTP(exp)){ + *v = OBJ_INT(exp); + } else { + err = stringof(exp, &s); + if(err) goto exit; + err = convert_atoul(s, &l); + *v = (int)l; + } + exit: + dprintf("< err=%d v=%d\n", err, *v); + return err; +} + +int addrof(Sxpr exp, uint32_t *v){ + char *h; + unsigned long a; + int err = 0; + dprintf(">\n"); + objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n"); + err = stringof(exp, &h); + if(err) goto exit; + if(get_host_address(h, &a)){ + *v = a; + } else { + err = -EINVAL; + } + exit: + dprintf("< err=%d v=%x\n", err, *v); + return err; +} + +int portof(Sxpr exp, uint16_t *v){ + char *s; + int err = 0; + dprintf(">\n"); + objprint(iostdout, exp, 0); IOStream_print(iostdout, "\n"); + if(INTP(exp)){ + *v = get_ul(exp); + *v = htons(*v); + } else { + unsigned long p; + err = stringof(exp, &s); + if(err) goto exit; + if(get_service_port(s, &p)){ + *v = p; + } else { + err = -EINVAL; + } + } + exit: + dprintf("< err=%d v=%u\n", err, *v); + return err; +} + +static inline struct in_addr inaddr(uint32_t addr){ + return (struct in_addr){ .s_addr = addr }; +} + +time_t stats(time_t t0, uint64_t offset, uint64_t memory, float *percent, float *rate){ + time_t t1 = time(NULL); + *percent = (offset * 100.0f) / memory; + t1 = time(NULL) - t0; + *rate = (t1 ? offset/(t1 * 1024.0f) : 0.0f); + return t1; +} + +/** Notify success or error. + * + * @param conn connection + * @param errcode error code + * @return 0 on success, error code otherwise + */ +int xfr_error(Conn *conn, int errcode){ + int err = 0; + if(!conn->out) return -ENOTCONN; + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_err); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(errcode)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + exit: + return err; +} + +/** Read a response message - error or ok. + * + * @param conn connection + * @return 0 on success, error code otherwise + */ +int xfr_response(Conn *conn){ + int err; + Sxpr sxpr; + + dprintf(">\n"); + if(!conn->out) return -ENOTCONN; + err = unpack_sxpr(conn->in, &sxpr); + if(err) goto exit; + if(sxpr_elementp(sxpr, oxfr_err)){ + int errcode; + err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode); + if(err) goto exit; + err = errcode; + } + exit: + dprintf("< err=%d\n", err); + return err; +} + +/** Get the initial hello message and check the protocol version. + * It is an error to receive anything other than a hello message + * with the correct protocol version. + * + * @param conn connection + * @return 0 on success, error code otherwise + */ +int xfr_hello(Conn *conn){ + int err; + uint32_t major = XFR_PROTO_MAJOR, minor = XFR_PROTO_MINOR; + uint32_t hello_major, hello_minor; + Sxpr sxpr; + if(!conn->in) return -ENOTCONN; + dprintf(">\n"); + err = unpack_sxpr(conn->in, &sxpr); + if(err) goto exit; + if(!sxpr_elementp(sxpr, oxfr_hello)){ + dprintf("> sxpr_elementp test failed\n"); + err = -EINVAL; + goto exit; + } + err = intof(sxpr_childN(sxpr, 0, ONONE), &hello_major); + if(err) goto exit; + err = intof(sxpr_childN(sxpr, 1, ONONE), &hello_minor); + if(err) goto exit; + if(hello_major != major || hello_minor != minor){ + eprintf("> Wanted protocol version %d.%d, got %d.%d", + major, minor, hello_major, hello_minor); + err = -EINVAL; + goto exit; + } + exit: + xfr_error(conn, err); + if(err){ + eprintf("> Hello failed: %d\n", err); + } + dprintf("< err=%d\n", err); + return err; +} + +/** Send the initial hello message. + * + * @param conn connection + * @param msg message + * @return 0 on success, error code otherwise + */ +int xfr_send_hello(Conn *conn){ + int err = 0; + dprintf(">\n"); + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_hello); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(XFR_PROTO_MAJOR)); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(XFR_PROTO_MINOR)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + IOStream_flush(conn->out); + dprintf("> xfr_response...\n"); + err = xfr_response(conn); + exit: + dprintf("< err=%d\n", err); + return err; +} + +int xfr_send_xfr(Conn *conn, uint32_t vmid){ + int err; + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_xfr); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(vmid)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + if(err) goto exit; + exit: + return err; +} + +int xfr_send_ok(Conn *conn, uint32_t vmid){ + int err = 0; + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_ok); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(vmid)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + exit: + return err; +} + +int xfr_send_suspend(Conn *conn, uint32_t vmid){ + int err = 0; + + err = pack_type(conn->out, T_CONS); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, oxfr_suspend); + if(err) goto exit; + err = pack_bool(conn->out, 1); + if(err) goto exit; + err = pack_sxpr(conn->out, mkint(vmid)); + if(err) goto exit; + err = pack_bool(conn->out, 0); + exit: + return err; +} + +/** Get vm state. Send transfer message. + * + * @param peer connection + * @param msg message + * @return 0 on success, error code otherwise + */ +int xfr_send_state(XfrState *state, Conn *xend, Conn *peer){ + int err = 0; + Sxpr sxpr; + + dprintf(">\n"); + XfrState_set_state(state, XFR_STATE); + // Send xfr message and the domain state. + err = xfr_send_xfr(peer, state->vmid); + if(err) goto exit; + err = xen_domain_snd(xend, peer->out, state->vmid, state->vmconfig, state->vmconfig_n); + if(err) goto exit; + IOStream_flush(peer->out); + // Read the response from the peer. + err = unpack_sxpr(peer->in, &sxpr); + if(err) goto exit; + if(sxpr_elementp(sxpr, oxfr_err)){ + // Error. + int errcode; + err = intof(sxpr_childN(sxpr, 0, ONONE), &errcode); + if(!err) err = errcode; + } else if(sxpr_elementp(sxpr, oxfr_ok)){ + // Ok - get the new domain id. + err = intof(sxpr_childN(sxpr, 0, ONONE), &state->vmid_new); + xfr_error(peer, err); + } else { + // Anything else is invalid. But it may be too late. + err = -EINVAL; + xfr_error(peer, err); + } + exit: + XfrState_set_err(state, err); + dprintf("< err=%d\n", err); + return err; +} + +/** Finish the transfer. + */ +int xfr_send_done(XfrState *state, Conn *xend){ + int err = 0; + int first_err = 0; + + first_err = XfrState_first_err(state); + if(first_err){ + XfrState_set_state(state, XFR_FAIL); + } else { + XfrState_set_state(state, XFR_DONE); + } + if(first_err){ + err = xfr_error(xend, first_err); + } else { + // Report new domain id to xend. + err = xfr_send_ok(xend, state->vmid_new); + } + + XfrState_set_err(state, err); + if(XfrState_first_err(state)){ + int s, serr; + + wprintf("> Transfer errors:\n"); + for(s = 0; s < XFR_MAX; s++){ + serr = state->state_err[s]; + if(!serr) continue; + wprintf("> state=%-12s err=%d\n", xfr_state_name(s), serr); + } + } else { + wprintf("> Transfer OK\n"); + } + dprintf("< err=%d\n", err); + return err; +} + +/** Migrate a vm to another node. + * + * @param xend connection + * @return 0 on success, error code otherwise + */ +int xfr_send(Args *args, XfrState *state, Conn *xend, uint32_t addr, uint32_t port){ + int err = 0; + Conn _peer = {}, *peer = &_peer; + int flags = 0; + struct in_addr xfr_addr; + uint16_t xfr_port; + time_t t0 = time(NULL), t1; + + dprintf(">\n"); + flags |= CONN_NOBUFFER; + if(args->compress){ + flags |= CONN_WRITE_COMPRESS; + } + xfr_addr.s_addr = addr; + xfr_port = port; + if(!xfr_port) xfr_port = htons(XFRD_PORT); + dprintf("> Xfr vmid=%u\n", state->vmid); + dprintf("> Xfr xfr_addr=%s:%d\n", inet_ntoa(xfr_addr), ntohs(xfr_port)); + err = Conn_connect(peer, flags, xfr_addr, xfr_port); + if(err) goto exit; + printf("\n"); + XfrState_set_state(state, XFR_HELLO); + // Send hello message. + err = xfr_send_hello(peer); + if(err) goto exit; + printf("\n"); + // Send vm state. + err = xfr_send_state(state, xend, peer); + if(err) goto exit; + if(args->compress){ + IOStream *zio = peer->out; + int plain_bytes = lzi_stream_plain_bytes(zio); + int comp_bytes = lzi_stream_comp_bytes(zio); + float ratio = lzi_stream_ratio(zio); + dprintf("> Compression: plain %d bytes, compressed %d bytes, ratio %3.2f\n", + plain_bytes, comp_bytes, ratio); + } + printf("\n"); + exit: + dprintf("> err=%d\n", err); + if(err && !XfrState_get_err(state)){ + XfrState_set_err(state, err); + } + Conn_close(peer); + if(!err){ + t1 = time(NULL) - t0; + dprintf("> Transfer complete in %lu seconds\n", t1); + } + dprintf("> done err=%d, notifying xend...\n", err); + xfr_send_done(state, xend); + dprintf("< err=%d\n", err); + return err; +} + +/** Save a vm to file. + */ +int xfr_save(Args *args, XfrState *state, Conn *xend, char *file){ + int err = 0; + IOStream *io = NULL; + + io = file_stream_fopen(file, "wb"); + if(!io){ + err = -EIO; + goto exit; + } + err = xen_domain_snd(xend, io, state->vmid, state->vmconfig, state->vmconfig_n); + exit: + if(io){ + IOStream_close(io); + IOStream_free(io); + } + return err; +} + +/** Accept the transfer of a vm from another node. + * + * @param peer connection + * @param msg message + * @return 0 on success, error code otherwise + */ +int xfr_recv(Args *args, XfrState *state, Conn *peer){ + int err = 0; + time_t t0 = time(NULL), t1; + + dprintf(">\n"); + err = xen_domain_rcv(peer->in, &state->vmid_new, &state->vmconfig, &state->vmconfig_n); + if(err) goto exit; + + err = xen_domain_configure(state->vmid_new, state->vmconfig, state->vmconfig_n); + if(err) goto exit; + + // Report new domain id to peer. + err = xfr_send_ok(peer, state->vmid_new); + if(err) goto exit; + // Get the final ok. + err = xfr_response(peer); + exit: + if(!err){ + t1 = time(NULL) - t0; + dprintf("> Transfer complete in %lu seconds\n", t1); + } + if(err){ + xfr_error(peer, err); + } + dprintf("< err=%d\n", err); + return err; +} + +/** Listen for a hello followed by a service request. + * The request can be from the local xend or from xfrd on another node. + * + * @param peersock socket + * @param peer_in peer address + * @return 0 on success, error code otherwise + */ +int xfrd_service(Args *args, int peersock, struct sockaddr_in peer_in){ + int err = 0; + Sxpr sxpr; + Conn _conn = {}, *conn = &_conn; + int flags = CONN_NOBUFFER; + + dprintf(">\n"); + err = Conn_init(conn, flags, peersock, peer_in); + if(err) goto exit; + err = xfr_hello(conn); + if(err) goto exit; + err = unpack_sxpr(conn->in, &sxpr); + if(err) goto exit; + if(sxpr_elementp(sxpr, oxfr_migrate)){ + // Migrate message from xend. + uint32_t addr; + uint16_t port; + XfrState _state = {}, *state = &_state; + int n = 0; + + dprintf("> xfr.migrate\n"); + err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid); + if(err) goto exit; + err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig); + if(err) goto exit; + state->vmconfig_n = strlen(state->vmconfig); + err = addrof(sxpr_childN(sxpr, n++, ONONE), &addr); + if(err) goto exit; + err = portof(sxpr_childN(sxpr, n++, ONONE), &port); + if(err) goto exit; + err = xfr_send(args, state, conn, addr, port); + + } else if(sxpr_elementp(sxpr, oxfr_save)){ + // Save message from xend. + char *file; + XfrState _state = {}, *state = &_state; + int n = 0; + + dprintf("> xfr.save\n"); + err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid); + if(err) goto exit; + err = stringof(sxpr_childN(sxpr, n++, ONONE), &state->vmconfig); + if(err) goto exit; + state->vmconfig_n = strlen(state->vmconfig); + err = stringof(sxpr_childN(sxpr, n++, ONONE), &file); + if(err) goto exit; + err = xfr_save(args, state, conn, file); + + } else if(sxpr_elementp(sxpr, oxfr_xfr)){ + // Xfr message from peer xfrd. + XfrState _state = {}, *state = &_state; + int n = 0; + + dprintf("> xfr.xfr\n"); + err = intof(sxpr_childN(sxpr, n++, ONONE), &state->vmid); + if(err) goto exit; + err = xfr_recv(args, state, conn); + + } else{ + // Anything else is invalid. + err = -EINVAL; + dprintf("> Invalid message: "); + objprint(iostderr, sxpr, 0); + IOStream_print(iostderr, "\n"); + xfr_error(conn, err); + } + exit: + Conn_close(conn); + dprintf("< err=%d\n", err); + return err; +} + +/** Accept an incoming connection. + * + * @param sock tcp socket + * @return 0 on success, error code otherwise + */ +int xfrd_accept(Args *args, int sock){ + struct sockaddr_in peer_in; + struct sockaddr *peer = (struct sockaddr *)&peer_in; + socklen_t peer_n = sizeof(peer_in); + int peersock; + pid_t pid; + int err = 0; + + dprintf(">\n"); + dprintf("> accept...\n"); + peersock = accept(sock, peer, &peer_n); + dprintf("> accept=%d\n", peersock); + if(peersock < 0){ + perror("accept"); + err = -errno; + goto exit; + } + iprintf("> Accepted connection from %s:%d\n", + inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port)); + pid = fork(); + if(pid > 0){ + // Parent, fork succeeded. + iprintf("> Forked child pid=%d\n", pid); + close(peersock); + } else if (pid < 0){ + // Parent, fork failed. + perror("fork"); + close(peersock); + } else { + // Child. + iprintf("> Xfr service for %s:%d\n", + inet_ntoa(peer_in.sin_addr), htons(peer_in.sin_port)); + err = xfrd_service(args, peersock, peer_in); + iprintf("> Xfr service err=%d\n", err); + shutdown(peersock, 2); + exit(err ? 1 : 0); + } + exit: + dprintf("< err=%d\n", err); + return err; +} + +/** Socket select loop. + * Accepts connections on the tcp socket. + * + * @param listen_sock tcp listen socket + * @return 0 on success, error code otherwise + */ +int xfrd_select(Args *args, int listen_sock){ + int err = 0; + SelectSet set = {}; + dprintf("> socks: %d\n", listen_sock); + while(1){ + SelectSet_zero(&set); + SelectSet_add_read(&set, listen_sock); + err = SelectSet_select(&set, NULL); + if(err < 0){ + if(errno == EINTR) continue; + perror("select"); + goto exit; + } + if(FD_ISSET(listen_sock, &set.rd)){ + xfrd_accept(args, listen_sock); + } + } + exit: + dprintf("< err=%d\n", err); + return err; +} + +/** Create a socket. + * + * @param args program arguments + * @param socktype socket type + * @param reuse whether to set SO_REUSEADDR + * @param val return value for the socket + * @return 0 on success, error code otherwise + */ +int create_socket(Args *args, int socktype, int reuse, int *val){ + int err = 0; + int sock = 0; + struct sockaddr_in addr_in; + struct sockaddr *addr = (struct sockaddr *)&addr_in; + socklen_t addr_n = sizeof(addr_in); + + dprintf(">\n"); + // Create socket and bind it. + sock = socket(AF_INET, socktype, 0); + if(sock < 0){ + err = -errno; + goto exit; + } + addr_in.sin_family = AF_INET; + addr_in.sin_addr.s_addr = INADDR_ANY; + addr_in.sin_port = args->port; + dprintf("> port=%d\n", ntohs(addr_in.sin_port)); + if(reuse){ + // Set socket option to reuse address. + int val = 1; + err = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); + if(err < 0){ + err = -errno; + perror("setsockopt"); + goto exit; + } + } + err = bind(sock, addr, addr_n); + if(err < 0){ + err = -errno; + perror("bind"); + goto exit; + } + exit: + *val = (err ? -1 : sock); + dprintf("< err=%d\n", err); + return err; +} + +/** Create the tcp listen socket. + * + * @param args program arguments + * @param val return value for the socket + * @return 0 on success, error code otherwise + */ +int xfrd_listen_socket(Args *args, int *val){ + int err = 0; + int sock; + dprintf(">\n"); + err = create_socket(args, SOCK_STREAM, 1, &sock); + if(err) goto exit; + dprintf("> listen...\n"); + err = listen(sock, 5); + if(err < 0){ + err = -errno; + perror("listen"); + goto exit; + } + exit: + *val = (err ? -1 : sock); + if(err) close(sock); + dprintf("< err=%d\n", err); + return err; +} + +/** Type for signal handling functions. */ +typedef void SignalAction(int code, siginfo_t *info, void *data); + +/** Handle SIGCHLD by getting child exit status. + * This prevents child processes being defunct. + * + * @param code signal code + * @param info signal info + * @param data + */ +void sigaction_SIGCHLD(int code, siginfo_t *info, void *data){ + int status; + pid_t pid; + //dprintf("> child_exit=%d waiting...\n", child_exit); + pid = wait(&status); + dprintf("> child pid=%d status=%d\n", pid, status); +} + +/** Handle SIGPIPE. + * + * @param code signal code + * @param info signal info + * @param data + */ +void sigaction_SIGPIPE(int code, siginfo_t *info, void *data){ + dprintf("> SIGPIPE\n"); + //fflush(stdout); + //fflush(stderr); + //exit(1); +} + +/** Handle SIGALRM. + * + * @param code signal code + * @param info signal info + * @param data + */ +void sigaction_SIGALRM(int code, siginfo_t *info, void *data){ + dprintf("> SIGALRM\n"); +} + +/** Install a handler for a signal. + * + * @param signum signal + * @param action handler + * @return 0 on success, error code otherwise + */ +int catch_signal(int signum, SignalAction *action){ + int err = 0; + struct sigaction sig = {}; + sig.sa_sigaction = action; + sig.sa_flags = SA_SIGINFO; + err = sigaction(signum, &sig, NULL); + if(err){ + perror("sigaction"); + } + return err; +} + +/** Transfer daemon main program. + * + * @param args program arguments + * @return 0 on success, error code otherwise + */ +int xfrd_main(Args *args){ + int err = 0; + int listen_sock; + + dprintf(">\n"); + catch_signal(SIGCHLD,sigaction_SIGCHLD); + catch_signal(SIGPIPE,sigaction_SIGPIPE); + catch_signal(SIGALRM,sigaction_SIGALRM); + err = xfrd_listen_socket(args, &listen_sock); + if(err) goto exit; + err = xfrd_select(args, listen_sock); + exit: + close(listen_sock); + dprintf("< err=%d\n", err); + return err; +} + +/** Parse command-line arguments and call the xfrd main program. + * + * @param arg argument count + * @param argv arguments + * @return 0 on success, 1 otherwise + */ +int main(int argc, char *argv[]){ + int err = 0; + int key = 0; + int long_index = 0; + + set_defaults(args); + while(1){ + key = getopt_long(argc, argv, short_opts, long_opts, &long_index); + if(key == -1) break; + switch(key){ + case OPT_PORT: + err = !convert_service_to_port(optarg, &args->port); + if(err) goto exit; + break; + case OPT_COMPRESS: + args->compress = TRUE; + break; + case OPT_HELP: + usage(0); + break; + case OPT_VERBOSE: + args->verbose = TRUE; + break; + case OPT_VERSION: + printf("> Version %d.%d\n", XFR_PROTO_MAJOR, XFR_PROTO_MINOR); + exit(0); + break; + default: + usage(EINVAL); + break; + } + } + xfr_init(); + err = xfrd_main(args); + exit: + if(err && key > 0){ + fprintf(stderr, "Error in arg %c\n", key); + } + return (err ? 1 : 0); +} + diff --git a/tools/xfrd/xfrd.h b/tools/xfrd/xfrd.h new file mode 100644 index 0000000000..2537fd67ab --- /dev/null +++ b/tools/xfrd/xfrd.h @@ -0,0 +1,14 @@ +#ifndef _XFRD_XFRD_H_ +#define _XFRD_XFRD_H_ + +/** Xend port in host order. */ +#define XEND_PORT 8001 + +/** Xfrd port in host order. */ +#define XFRD_PORT 8002 + +/** Protocol version. */ +#define XFR_PROTO_MAJOR 1 +#define XFR_PROTO_MINOR 0 + +#endif diff --git a/tools/xfrd/xfrdClient.py b/tools/xfrd/xfrdClient.py new file mode 100755 index 0000000000..b8a3554729 --- /dev/null +++ b/tools/xfrd/xfrdClient.py @@ -0,0 +1,127 @@ +#!/bin/env python +""" +Test client for the migration daemon (xfrd). + +Author: Mike Wray + +""" +import getopt +import sys +import os +from socket import * +import StringIO + +sys.path.append("/home/mjw/repos-bk/xeno-unstable.bk/tools/python") + +import xen.xend.sxp as sxp +from xen.xend.packing import SxpPacker, SxpUnpacker + +XFRD_PORT = 8002 + +verbose = 0 + +class TCPClient: + + def __init__(self, host, port): + print ">TCPClient" + self.sock = socket(AF_INET, SOCK_STREAM, 0) + print ">TCPClient sock=", self.sock + print ">TCPClient> connect ", host, port + v = self.sock.connect((host, port)) + print ">TCPClient> connect=", v + # Send plain header (no gzip). + #self.sock.send("\0\0") + + self.sockin = self.sock.makefile("r") + self.sockout = self.sock.makefile("w") + self.packer = SxpPacker(self.sockout) + self.unpacker = SxpUnpacker(self.sockin) + #pass + + def request(self, req): + print "request>", req + self.packer.pack(req) + self.sockout.flush() + print "request<" + + def request_hello(self): + self.request(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR]) + + def request_migrate(self, vmid, vhost, vport, vmconfig='(vm)'): + self.request(['xfr.migrate', vmid, vmconfig, vhost, vport]) + + def read(self): + while(1): + v = self.unpacker.unpack() + print 'read>', v + if v[0] == 'xfr.err' and v[1]: return + if v[0] == 'xfr.ok': return + +XFR_PROTO_MAJOR = 1 +XFR_PROTO_MINOR = 0 + +host_default = "localhost" +port_default = XFRD_PORT +vhost_default = "localhost" +vport_default = 8003 +vmid_default = 1 + +# Short options. Options followed by ':' need a parameter. +short_opts = 'h' + +# Long options. Options ending in '=' need a parameter. +long_opts = [ 'host=', 'port=', 'vhost=', 'vport=', 'vmid=', 'verbose', 'help'] + +def usage(err=None): + if err: + out = sys.stderr + else: + out = sys.stdout + print >> out, 'Usage: %s [options] [command...]\n' % sys.argv[0] + print >> out, '--host \n\tHost to initiate transfer on. Default %s.' % host_default + print >> out, '--port \n\tPort to initiate transfer on. Default %d.' % port_default + print >> out, '--vhost \n\tHost to transfer VM to. Default %s.' % vhost_default + print >> out, '--vport \n\tPort to transfer VM to. Default %d.' % vport_default + print >> out, '--vmid \n\tVM id. Default %d.' % vmid_default + print >> out, '--help\n\tPrint help.' + +def main(argv): + global verbose + host = host_default + port = port_default + vhost = vhost_default + vport = vport_default + vmid = vmid_default + + try: + opts, args = getopt.getopt(argv[1:], short_opts, long_opts) + except getopt.GetoptError, ex: + print >>sys.stderr, 'Error:', ex + usage(1) + sys.exit(1) + + for key, val in opts: + if key == '--help': + usage() + sys.exit(0) + elif key == '--host': + host = val + elif key == '--port': + port = int(val) + elif key == '--vhost': + vhost = val + elif key == '--vport': + vport = int(val) + elif key == '--vmid': + vmid = int(val) + + print "host=%s port=%d" % (host, port) + print "vhost=%s vport=%d vmid=%d" % (vhost, vport, vmid) + client = TCPClient(gethostbyname(host), port) + client.request_hello() + client.request_migrate(vmid, gethostbyname(vhost), vport) + client.read() + +if __name__ == '__main__': + main(sys.argv) + -- 2.30.2